from typing import Annotated, Literal, Optional from pydantic import ( AliasChoices, Field, HttpUrl, NegativeInt, NonNegativeInt, PositiveFloat, PositiveInt, computed_field, ) from pydantic_settings import BaseSettings from configs.feature.hosted_service import HostedServiceConfig class SecurityConfig(BaseSettings): """ Security-related configurations for the application """ SECRET_KEY: str = Field( description="Secret key for secure session cookie signing." "Make sure you are changing this key for your deployment with a strong key." "Generate a strong key using `openssl rand -base64 42` or set via the `SECRET_KEY` environment variable.", default="", ) RESET_PASSWORD_TOKEN_EXPIRY_MINUTES: PositiveInt = Field( description="Duration in minutes for which a password reset token remains valid", default=5, ) LOGIN_DISABLED: bool = Field( description="Whether to disable login checks", default=False, ) ADMIN_API_KEY_ENABLE: bool = Field( description="Whether to enable admin api key for authentication", default=False, ) ADMIN_API_KEY: Optional[str] = Field( description="admin api key for authentication", default=None, ) class AppExecutionConfig(BaseSettings): """ Configuration parameters for application execution """ APP_MAX_EXECUTION_TIME: PositiveInt = Field( description="Maximum allowed execution time for the application in seconds", default=1200, ) APP_MAX_ACTIVE_REQUESTS: NonNegativeInt = Field( description="Maximum number of concurrent active requests per app (0 for unlimited)", default=0, ) class CodeExecutionSandboxConfig(BaseSettings): """ Configuration for the code execution sandbox environment """ CODE_EXECUTION_ENDPOINT: HttpUrl = Field( description="URL endpoint for the code execution service", default="http://sandbox:8194", ) CODE_EXECUTION_API_KEY: str = Field( description="API key for accessing the code execution service", default="dify-sandbox", ) CODE_EXECUTION_CONNECT_TIMEOUT: Optional[float] = Field( description="Connection timeout in seconds for code execution requests", default=10.0, ) CODE_EXECUTION_READ_TIMEOUT: Optional[float] = Field( description="Read timeout in seconds for code execution requests", default=60.0, ) CODE_EXECUTION_WRITE_TIMEOUT: Optional[float] = Field( description="Write timeout in seconds for code execution request", default=10.0, ) CODE_MAX_NUMBER: PositiveInt = Field( description="Maximum allowed numeric value in code execution", default=9223372036854775807, ) CODE_MIN_NUMBER: NegativeInt = Field( description="Minimum allowed numeric value in code execution", default=-9223372036854775807, ) CODE_MAX_DEPTH: PositiveInt = Field( description="Maximum allowed depth for nested structures in code execution", default=5, ) CODE_MAX_PRECISION: PositiveInt = Field( description="mMaximum number of decimal places for floating-point numbers in code execution", default=20, ) CODE_MAX_STRING_LENGTH: PositiveInt = Field( description="Maximum allowed length for strings in code execution", default=80000, ) CODE_MAX_STRING_ARRAY_LENGTH: PositiveInt = Field( description="Maximum allowed length for string arrays in code execution", default=30, ) CODE_MAX_OBJECT_ARRAY_LENGTH: PositiveInt = Field( description="Maximum allowed length for object arrays in code execution", default=30, ) CODE_MAX_NUMBER_ARRAY_LENGTH: PositiveInt = Field( description="Maximum allowed length for numeric arrays in code execution", default=1000, ) class EndpointConfig(BaseSettings): """ Configuration for various application endpoints and URLs """ CONSOLE_API_URL: str = Field( description="Base URL for the console API," "used for login authentication callback or notion integration callbacks", default="", ) CONSOLE_WEB_URL: str = Field( description="Base URL for the console web interface," "used for frontend references and CORS configuration", default="", ) SERVICE_API_URL: str = Field( description="Base URL for the service API, displayed to users for API access", default="", ) APP_WEB_URL: str = Field( description="Base URL for the web application, used for frontend references", default="", ) class FileAccessConfig(BaseSettings): """ Configuration for file access and handling """ FILES_URL: str = Field( description="Base URL for file preview or download," " used for frontend display and multi-model inputs" "Url is signed and has expiration time.", validation_alias=AliasChoices("FILES_URL", "CONSOLE_API_URL"), alias_priority=1, default="", ) FILES_ACCESS_TIMEOUT: int = Field( description="Expiration time in seconds for file access URLs", default=300, ) class FileUploadConfig(BaseSettings): """ Configuration for file upload limitations """ UPLOAD_FILE_SIZE_LIMIT: NonNegativeInt = Field( description="Maximum allowed file size for uploads in megabytes", default=15, ) UPLOAD_FILE_BATCH_LIMIT: NonNegativeInt = Field( description="Maximum number of files allowed in a single upload batch", default=5, ) UPLOAD_IMAGE_FILE_SIZE_LIMIT: NonNegativeInt = Field( description="Maximum allowed image file size for uploads in megabytes", default=10, ) UPLOAD_VIDEO_FILE_SIZE_LIMIT: NonNegativeInt = Field( description="video file size limit in Megabytes for uploading files", default=100, ) UPLOAD_AUDIO_FILE_SIZE_LIMIT: NonNegativeInt = Field( description="audio file size limit in Megabytes for uploading files", default=50, ) BATCH_UPLOAD_LIMIT: NonNegativeInt = Field( description="Maximum number of files allowed in a batch upload operation", default=20, ) WORKFLOW_FILE_UPLOAD_LIMIT: PositiveInt = Field( description="Maximum number of files allowed in a workflow upload operation", default=10, ) class HttpConfig(BaseSettings): """ HTTP-related configurations for the application """ API_COMPRESSION_ENABLED: bool = Field( description="Enable or disable gzip compression for HTTP responses", default=False, ) inner_CONSOLE_CORS_ALLOW_ORIGINS: str = Field( description="Comma-separated list of allowed origins for CORS in the console", validation_alias=AliasChoices("CONSOLE_CORS_ALLOW_ORIGINS", "CONSOLE_WEB_URL"), default="", ) @computed_field @property def CONSOLE_CORS_ALLOW_ORIGINS(self) -> list[str]: return self.inner_CONSOLE_CORS_ALLOW_ORIGINS.split(",") inner_WEB_API_CORS_ALLOW_ORIGINS: str = Field( description="", validation_alias=AliasChoices("WEB_API_CORS_ALLOW_ORIGINS"), default="*", ) @computed_field @property def WEB_API_CORS_ALLOW_ORIGINS(self) -> list[str]: return self.inner_WEB_API_CORS_ALLOW_ORIGINS.split(",") HTTP_REQUEST_MAX_CONNECT_TIMEOUT: Annotated[ PositiveInt, Field(ge=10, description="Maximum connection timeout in seconds for HTTP requests") ] = 10 HTTP_REQUEST_MAX_READ_TIMEOUT: Annotated[ PositiveInt, Field(ge=60, description="Maximum read timeout in seconds for HTTP requests") ] = 60 HTTP_REQUEST_MAX_WRITE_TIMEOUT: Annotated[ PositiveInt, Field(ge=10, description="Maximum write timeout in seconds for HTTP requests") ] = 20 HTTP_REQUEST_NODE_MAX_BINARY_SIZE: PositiveInt = Field( description="Maximum allowed size in bytes for binary data in HTTP requests", default=10 * 1024 * 1024, ) HTTP_REQUEST_NODE_MAX_TEXT_SIZE: PositiveInt = Field( description="Maximum allowed size in bytes for text data in HTTP requests", default=1 * 1024 * 1024, ) SSRF_PROXY_HTTP_URL: Optional[str] = Field( description="Proxy URL for HTTP requests to prevent Server-Side Request Forgery (SSRF)", default=None, ) SSRF_PROXY_HTTPS_URL: Optional[str] = Field( description="Proxy URL for HTTPS requests to prevent Server-Side Request Forgery (SSRF)", default=None, ) RESPECT_XFORWARD_HEADERS_ENABLED: bool = Field( description="Enable or disable the X-Forwarded-For Proxy Fix middleware from Werkzeug" " to respect X-* headers to redirect clients", default=False, ) class InnerAPIConfig(BaseSettings): """ Configuration for internal API functionality """ INNER_API: bool = Field( description="Enable or disable the internal API", default=False, ) INNER_API_KEY: Optional[str] = Field( description="API key for accessing the internal API", default=None, ) class LoggingConfig(BaseSettings): """ Configuration for application logging """ LOG_LEVEL: str = Field( description="Logging level, default to INFO. Set to ERROR for production environments.", default="INFO", ) LOG_FILE: Optional[str] = Field( description="File path for log output.", default=None, ) LOG_FILE_MAX_SIZE: PositiveInt = Field( description="Maximum file size for file rotation retention, the unit is megabytes (MB)", default=20, ) LOG_FILE_BACKUP_COUNT: PositiveInt = Field( description="Maximum file backup count file rotation retention", default=5, ) LOG_FORMAT: str = Field( description="Format string for log messages", default="%(asctime)s.%(msecs)03d %(levelname)s [%(threadName)s] [%(filename)s:%(lineno)d] - %(message)s", ) LOG_DATEFORMAT: Optional[str] = Field( description="Date format string for log timestamps", default=None, ) LOG_TZ: Optional[str] = Field( description="Timezone for log timestamps (e.g., 'America/New_York')", default=None, ) class ModelLoadBalanceConfig(BaseSettings): """ Configuration for model load balancing """ MODEL_LB_ENABLED: bool = Field( description="Enable or disable load balancing for models", default=False, ) class BillingConfig(BaseSettings): """ Configuration for platform billing features """ BILLING_ENABLED: bool = Field( description="Enable or disable billing functionality", default=False, ) class UpdateConfig(BaseSettings): """ Configuration for application update checks """ CHECK_UPDATE_URL: str = Field( description="URL to check for application updates", default="https://updates.dify.ai", ) class WorkflowConfig(BaseSettings): """ Configuration for workflow execution """ WORKFLOW_MAX_EXECUTION_STEPS: PositiveInt = Field( description="Maximum number of steps allowed in a single workflow execution", default=500, ) WORKFLOW_MAX_EXECUTION_TIME: PositiveInt = Field( description="Maximum execution time in seconds for a single workflow", default=1200, ) WORKFLOW_CALL_MAX_DEPTH: PositiveInt = Field( description="Maximum allowed depth for nested workflow calls", default=5, ) MAX_VARIABLE_SIZE: PositiveInt = Field( description="Maximum size in bytes for a single variable in workflows. Default to 200 KB.", default=200 * 1024, ) class AuthConfig(BaseSettings): """ Configuration for authentication and OAuth """ OAUTH_REDIRECT_PATH: str = Field( description="Redirect path for OAuth authentication callbacks", default="/console/api/oauth/authorize", ) GITHUB_CLIENT_ID: Optional[str] = Field( description="GitHub OAuth client ID", default=None, ) GITHUB_CLIENT_SECRET: Optional[str] = Field( description="GitHub OAuth client secret", default=None, ) GOOGLE_CLIENT_ID: Optional[str] = Field( description="Google OAuth client ID", default=None, ) GOOGLE_CLIENT_SECRET: Optional[str] = Field( description="Google OAuth client secret", default=None, ) ACCESS_TOKEN_EXPIRE_MINUTES: PositiveInt = Field( description="Expiration time for access tokens in minutes", default=60, ) class ModerationConfig(BaseSettings): """ Configuration for content moderation """ MODERATION_BUFFER_SIZE: PositiveInt = Field( description="Size of the buffer for content moderation processing", default=300, ) class ToolConfig(BaseSettings): """ Configuration for tool management """ TOOL_ICON_CACHE_MAX_AGE: PositiveInt = Field( description="Maximum age in seconds for caching tool icons", default=3600, ) class MailConfig(BaseSettings): """ Configuration for email services """ MAIL_TYPE: Optional[str] = Field( description="Email service provider type ('smtp' or 'resend'), default to None.", default=None, ) MAIL_DEFAULT_SEND_FROM: Optional[str] = Field( description="Default email address to use as the sender", default=None, ) RESEND_API_KEY: Optional[str] = Field( description="API key for Resend email service", default=None, ) RESEND_API_URL: Optional[str] = Field( description="API URL for Resend email service", default=None, ) SMTP_SERVER: Optional[str] = Field( description="SMTP server hostname", default=None, ) SMTP_PORT: Optional[int] = Field( description="SMTP server port number", default=465, ) SMTP_USERNAME: Optional[str] = Field( description="Username for SMTP authentication", default=None, ) SMTP_PASSWORD: Optional[str] = Field( description="Password for SMTP authentication", default=None, ) SMTP_USE_TLS: bool = Field( description="Enable TLS encryption for SMTP connections", default=False, ) SMTP_OPPORTUNISTIC_TLS: bool = Field( description="Enable opportunistic TLS for SMTP connections", default=False, ) EMAIL_SEND_IP_LIMIT_PER_MINUTE: PositiveInt = Field( description="Maximum number of emails allowed to be sent from the same IP address in a minute", default=50, ) class RagEtlConfig(BaseSettings): """ Configuration for RAG ETL processes """ # TODO: This config is not only for rag etl, it is also for file upload, we should move it to file upload config ETL_TYPE: str = Field( description="RAG ETL type ('dify' or 'Unstructured'), default to 'dify'", default="dify", ) KEYWORD_DATA_SOURCE_TYPE: str = Field( description="Data source type for keyword extraction" " ('database' or other supported types), default to 'database'", default="database", ) UNSTRUCTURED_API_URL: Optional[str] = Field( description="API URL for Unstructured.io service", default=None, ) UNSTRUCTURED_API_KEY: Optional[str] = Field( description="API key for Unstructured.io service", default=None, ) class DataSetConfig(BaseSettings): """ Configuration for dataset management """ PLAN_SANDBOX_CLEAN_DAY_SETTING: PositiveInt = Field( description="Interval in days for dataset cleanup operations - plan: sandbox", default=30, ) PLAN_PRO_CLEAN_DAY_SETTING: PositiveInt = Field( description="Interval in days for dataset cleanup operations - plan: pro and team", default=7, ) DATASET_OPERATOR_ENABLED: bool = Field( description="Enable or disable dataset operator functionality", default=False, ) TIDB_SERVERLESS_NUMBER: PositiveInt = Field( description="number of tidb serverless cluster", default=500, ) class WorkspaceConfig(BaseSettings): """ Configuration for workspace management """ INVITE_EXPIRY_HOURS: PositiveInt = Field( description="Expiration time in hours for workspace invitation links", default=72, ) class IndexingConfig(BaseSettings): """ Configuration for indexing operations """ INDEXING_MAX_SEGMENTATION_TOKENS_LENGTH: PositiveInt = Field( description="Maximum token length for text segmentation during indexing", default=1000, ) class ImageFormatConfig(BaseSettings): MULTIMODAL_SEND_IMAGE_FORMAT: Literal["base64", "url"] = Field( description="Format for sending images in multimodal contexts ('base64' or 'url'), default is base64", default="base64", ) class CeleryBeatConfig(BaseSettings): CELERY_BEAT_SCHEDULER_TIME: int = Field( description="Interval in days for Celery Beat scheduler execution, default to 1 day", default=1, ) class PositionConfig(BaseSettings): POSITION_PROVIDER_PINS: str = Field( description="Comma-separated list of pinned model providers", default="", ) POSITION_PROVIDER_INCLUDES: str = Field( description="Comma-separated list of included model providers", default="", ) POSITION_PROVIDER_EXCLUDES: str = Field( description="Comma-separated list of excluded model providers", default="", ) POSITION_TOOL_PINS: str = Field( description="Comma-separated list of pinned tools", default="", ) POSITION_TOOL_INCLUDES: str = Field( description="Comma-separated list of included tools", default="", ) POSITION_TOOL_EXCLUDES: str = Field( description="Comma-separated list of excluded tools", default="", ) @computed_field def POSITION_PROVIDER_PINS_LIST(self) -> list[str]: return [item.strip() for item in self.POSITION_PROVIDER_PINS.split(",") if item.strip() != ""] @computed_field def POSITION_PROVIDER_INCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_PROVIDER_INCLUDES.split(",") if item.strip() != ""} @computed_field def POSITION_PROVIDER_EXCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_PROVIDER_EXCLUDES.split(",") if item.strip() != ""} @computed_field def POSITION_TOOL_PINS_LIST(self) -> list[str]: return [item.strip() for item in self.POSITION_TOOL_PINS.split(",") if item.strip() != ""] @computed_field def POSITION_TOOL_INCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_TOOL_INCLUDES.split(",") if item.strip() != ""} @computed_field def POSITION_TOOL_EXCLUDES_SET(self) -> set[str]: return {item.strip() for item in self.POSITION_TOOL_EXCLUDES.split(",") if item.strip() != ""} class LoginConfig(BaseSettings): ENABLE_EMAIL_CODE_LOGIN: bool = Field( description="whether to enable email code login", default=False, ) ENABLE_EMAIL_PASSWORD_LOGIN: bool = Field( description="whether to enable email password login", default=True, ) ENABLE_SOCIAL_OAUTH_LOGIN: bool = Field( description="whether to enable github/google oauth login", default=False, ) EMAIL_CODE_LOGIN_TOKEN_EXPIRY_MINUTES: PositiveInt = Field( description="expiry time in minutes for email code login token", default=5, ) ALLOW_REGISTER: bool = Field( description="whether to enable register", default=False, ) ALLOW_CREATE_WORKSPACE: bool = Field( description="whether to enable create workspace", default=False, ) class FeatureConfig( # place the configs in alphabet order AppExecutionConfig, AuthConfig, # Changed from OAuthConfig to AuthConfig BillingConfig, CodeExecutionSandboxConfig, DataSetConfig, EndpointConfig, FileAccessConfig, FileUploadConfig, HttpConfig, ImageFormatConfig, InnerAPIConfig, IndexingConfig, LoggingConfig, MailConfig, ModelLoadBalanceConfig, ModerationConfig, PositionConfig, RagEtlConfig, SecurityConfig, ToolConfig, UpdateConfig, WorkflowConfig, WorkspaceConfig, LoginConfig, # hosted services config HostedServiceConfig, CeleryBeatConfig, ): pass