- Add eveai-tem secret (Scaleway Transactional Email) to allow sending emails - Adapted security URLs - Certification problem in regions solved - Redis insight added to tools in k8s - Introduced new way of connection pooling for Redis - TRA-79 - intrernal server error bij registreren catalog
419 lines
15 KiB
Python
419 lines
15 KiB
Python
import os
|
|
from os import environ, path
|
|
from datetime import timedelta
|
|
import redis
|
|
import ssl
|
|
import tempfile
|
|
from ipaddress import ip_address
|
|
|
|
from common.utils.prompt_loader import load_prompt_templates
|
|
|
|
basedir = path.abspath(path.dirname(__file__))
|
|
|
|
|
|
class Config(object):
|
|
DEBUG = False
|
|
DEVELOPMENT = False
|
|
SECRET_KEY = environ.get('SECRET_KEY')
|
|
COMPONENT_NAME = environ.get('COMPONENT_NAME')
|
|
|
|
# Database Settings ---------------------------------------------------------------------------
|
|
DB_HOST = environ.get('DB_HOST')
|
|
DB_USER = environ.get('DB_USER')
|
|
DB_PASS = environ.get('DB_PASS')
|
|
DB_NAME = environ.get('DB_NAME')
|
|
DB_PORT = environ.get('DB_PORT')
|
|
SQLALCHEMY_DATABASE_URI = f'postgresql+pg8000://{DB_USER}:{DB_PASS}@{DB_HOST}:{DB_PORT}/{DB_NAME}'
|
|
SQLALCHEMY_BINDS = {'public': SQLALCHEMY_DATABASE_URI}
|
|
|
|
# Redis Settings ------------------------------------------------------------------------------
|
|
REDIS_URL = environ.get('REDIS_URL')
|
|
REDIS_PORT = environ.get('REDIS_PORT', '6379')
|
|
REDIS_USER = environ.get('REDIS_USER')
|
|
REDIS_PASS = environ.get('REDIS_PASS')
|
|
REDIS_CERT_DATA = environ.get('REDIS_CERT')
|
|
REDIS_SCHEME = None
|
|
|
|
# Determine if REDIS_URL is an IP; use it to control hostname checking
|
|
REDIS_IS_IP = False
|
|
try:
|
|
ip_address(REDIS_URL)
|
|
REDIS_IS_IP = True
|
|
except Exception:
|
|
REDIS_IS_IP = False
|
|
REDIS_SSL_CHECK_HOSTNAME = not REDIS_IS_IP
|
|
|
|
# Write CA once to a file, expose path
|
|
REDIS_CA_CERT_PATH = None
|
|
if REDIS_CERT_DATA:
|
|
_tmp = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.pem')
|
|
_tmp.write(REDIS_CERT_DATA)
|
|
_tmp.flush()
|
|
_tmp.close()
|
|
REDIS_CA_CERT_PATH = _tmp.name
|
|
|
|
if not REDIS_CERT_DATA: # We are in a simple dev/test environment
|
|
REDIS_SCHEME = 'redis'
|
|
REDIS_BASE_URI = f'redis://{REDIS_URL}:{REDIS_PORT}'
|
|
else: # We are in a scaleway environment, providing name, user and certificate
|
|
REDIS_SCHEME = 'rediss'
|
|
REDIS_BASE_URI = f'rediss://{REDIS_USER}:{REDIS_PASS}@{REDIS_URL}:{REDIS_PORT}'
|
|
|
|
# Central SSL options dict for reuse (Celery/Dogpile/etc.)
|
|
REDIS_SSL_OPTIONS = None
|
|
if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
|
|
REDIS_SSL_OPTIONS = {
|
|
'ssl_cert_reqs': ssl.CERT_REQUIRED,
|
|
'ssl_ca_certs': REDIS_CA_CERT_PATH,
|
|
'ssl_check_hostname': REDIS_SSL_CHECK_HOSTNAME,
|
|
}
|
|
|
|
REDIS_PREFIXES = {
|
|
'celery_app': 'celery:app:',
|
|
'celery_chat': 'celery:chat:',
|
|
'session': 'session:',
|
|
'cache_workers': 'cache:workers:',
|
|
'pubsub_execution': 'pubsub:execution:',
|
|
'startup_ops': 'startup:ops:',
|
|
}
|
|
|
|
# Celery Redis settings
|
|
CELERY_BROKER_URL = f'{REDIS_BASE_URI}/0'
|
|
CELERY_RESULT_BACKEND = f'{REDIS_BASE_URI}/0'
|
|
CELERY_BROKER_URL_CHAT = f'{REDIS_BASE_URI}/0'
|
|
CELERY_RESULT_BACKEND_CHAT = f'{REDIS_BASE_URI}/0'
|
|
|
|
# SSE PubSub settings
|
|
SPECIALIST_EXEC_PUBSUB = f'{REDIS_BASE_URI}/0'
|
|
|
|
# eveai_model cache Redis setting
|
|
MODEL_CACHE_URL = f'{REDIS_BASE_URI}/0'
|
|
|
|
# Session Settings with Redis -----------------------------------------------------------------
|
|
SESSION_TYPE = 'redis'
|
|
SESSION_PERMANENT = True
|
|
SESSION_USE_SIGNER = True
|
|
PERMANENT_SESSION_LIFETIME = timedelta(minutes=60)
|
|
SESSION_REFRESH_EACH_REQUEST = True
|
|
# Configure SESSION_REDIS with SSL when cert is provided
|
|
if REDIS_CERT_DATA and REDIS_CA_CERT_PATH:
|
|
SESSION_REDIS = redis.from_url(
|
|
f'{REDIS_BASE_URI}/0', # REDIS_BASE_URI is reeds rediss://user:pass@host:port
|
|
ssl_cert_reqs=ssl.CERT_REQUIRED,
|
|
ssl_ca_certs=REDIS_CA_CERT_PATH,
|
|
ssl_check_hostname=REDIS_SSL_CHECK_HOSTNAME,
|
|
)
|
|
else:
|
|
SESSION_REDIS = redis.from_url(f'{REDIS_BASE_URI}/0')
|
|
SESSION_KEY_PREFIX = f'session_{COMPONENT_NAME}:'
|
|
SESSION_COOKIE_NAME = f'{COMPONENT_NAME}_session'
|
|
SESSION_COOKIE_DOMAIN = None # Laat Flask dit automatisch bepalen
|
|
SESSION_COOKIE_PATH = '/'
|
|
SESSION_COOKIE_HTTPONLY = True
|
|
SESSION_COOKIE_SECURE = False # True voor production met HTTPS
|
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
|
REMEMBER_COOKIE_SAMESITE = 'strict'
|
|
|
|
WTF_CSRF_ENABLED = True
|
|
WTF_CSRF_TIME_LIMIT = None
|
|
WTF_CSRF_SSL_STRICT = False # Set to True if using HTTPS
|
|
|
|
# flask-security-too settings -----------------------------------------------------------------
|
|
# SECURITY_URL_PREFIX = '/admin'
|
|
SECURITY_LOGIN_URL = '/admin/login'
|
|
SECURITY_LOGOUT_URL = '/admin/logout'
|
|
# SECURITY_REGISTER_URL = '/admin/register'
|
|
# SECURITY_RESET_URL = '/admin/reset'
|
|
# SECURITY_CHANGE_URL = '/admin/change'
|
|
# SECURITY_POST_LOGIN_VIEW = '/admin/user/tenant_overview'
|
|
# SECURITY_POST_LOGOUT_VIEW = '/admin'
|
|
# SECURITY_POST_REGISTER_VIEW = '/admin/user/tenant_overview'
|
|
# SECURITY_POST_RESET_VIEW = '/admin/login'
|
|
# SECURITY_POST_CHANGE_VIEW = '/admin/login'
|
|
# SECURITY_BLUEPRINT_NAME = 'security_bp'
|
|
SECURITY_PASSWORD_SALT = environ.get('SECURITY_PASSWORD_SALT')
|
|
SECURITY_CONFIRMABLE = True
|
|
SECURITY_TRACKABLE = True
|
|
SECURITY_PASSWORD_COMPLEXITY_CHECKER = 'zxcvbn'
|
|
SECURITY_POST_LOGIN_VIEW = '/admin/user/tenant_overview'
|
|
SECURITY_RECOVERABLE = True
|
|
SECURITY_EMAIL_SENDER = "eveai_super@flow-it.net"
|
|
SECURITY_EMAIL_SUBJECT_PASSWORD_RESET = 'Reset Your Password'
|
|
SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE = 'Your Password Has Been Reset'
|
|
SECURITY_EMAIL_PLAINTEXT = False
|
|
SECURITY_EMAIL_HTML = True
|
|
SECURITY_SESSION_PROTECTION = 'basic' # of 'basic' als 'strong' problemen geeft
|
|
SECURITY_REMEMBER_TOKEN_VALIDITY = timedelta(minutes=60) # Zelfde als session lifetime
|
|
SECURITY_AUTO_LOGIN_AFTER_CONFIRM = True
|
|
SECURITY_AUTO_LOGIN_AFTER_RESET = True
|
|
|
|
# Ensure Flask-Security-Too is handling CSRF tokens when behind a proxy
|
|
SECURITY_CSRF_PROTECT_MECHANISMS = ['session']
|
|
SECURITY_CSRF_COOKIE_NAME = 'XSRF-TOKEN'
|
|
SECURITY_CSRF_HEADER = 'X-XSRF-TOKEN'
|
|
WTF_CSRF_CHECK_DEFAULT = False
|
|
|
|
# file upload settings ------------------------------------------------------------------------
|
|
MAX_CONTENT_LENGTH = 50 * 1024 * 1024
|
|
|
|
# supported languages -------------------------------------------------------------------------
|
|
SUPPORTED_LANGUAGE_DETAILS = {
|
|
"English": {
|
|
"iso 639-1": "en",
|
|
"iso 639-2": "eng",
|
|
"iso 639-3": "eng",
|
|
"flag": "🇬🇧"
|
|
},
|
|
"French": {
|
|
"iso 639-1": "fr",
|
|
"iso 639-2": "fre", # of 'fra'
|
|
"iso 639-3": "fra",
|
|
"flag": "🇫🇷"
|
|
},
|
|
"German": {
|
|
"iso 639-1": "de",
|
|
"iso 639-2": "ger", # of 'deu'
|
|
"iso 639-3": "deu",
|
|
"flag": "🇩🇪"
|
|
},
|
|
"Spanish": {
|
|
"iso 639-1": "es",
|
|
"iso 639-2": "spa",
|
|
"iso 639-3": "spa",
|
|
"flag": "🇪🇸"
|
|
},
|
|
"Italian": {
|
|
"iso 639-1": "it",
|
|
"iso 639-2": "ita",
|
|
"iso 639-3": "ita",
|
|
"flag": "🇮🇹"
|
|
},
|
|
"Portuguese": {
|
|
"iso 639-1": "pt",
|
|
"iso 639-2": "por",
|
|
"iso 639-3": "por",
|
|
"flag": "🇵🇹"
|
|
},
|
|
"Dutch": {
|
|
"iso 639-1": "nl",
|
|
"iso 639-2": "dut", # of 'nld'
|
|
"iso 639-3": "nld",
|
|
"flag": "🇳🇱"
|
|
},
|
|
"Russian": {
|
|
"iso 639-1": "ru",
|
|
"iso 639-2": "rus",
|
|
"iso 639-3": "rus",
|
|
"flag": "🇷🇺"
|
|
},
|
|
"Chinese": {
|
|
"iso 639-1": "zh",
|
|
"iso 639-2": "chi", # of 'zho'
|
|
"iso 639-3": "zho",
|
|
"flag": "🇨🇳"
|
|
},
|
|
"Japanese": {
|
|
"iso 639-1": "ja",
|
|
"iso 639-2": "jpn",
|
|
"iso 639-3": "jpn",
|
|
"flag": "🇯🇵"
|
|
},
|
|
"Korean": {
|
|
"iso 639-1": "ko",
|
|
"iso 639-2": "kor",
|
|
"iso 639-3": "kor",
|
|
"flag": "🇰🇷"
|
|
},
|
|
"Arabic": {
|
|
"iso 639-1": "ar",
|
|
"iso 639-2": "ara",
|
|
"iso 639-3": "ara",
|
|
"flag": "🇸🇦"
|
|
},
|
|
"Hindi": {
|
|
"iso 639-1": "hi",
|
|
"iso 639-2": "hin",
|
|
"iso 639-3": "hin",
|
|
"flag": "🇮🇳"
|
|
},
|
|
}
|
|
|
|
# Afgeleide taalconstanten
|
|
SUPPORTED_LANGUAGES = [lang_details["iso 639-1"] for lang_details in SUPPORTED_LANGUAGE_DETAILS.values()]
|
|
SUPPORTED_LANGUAGES_FULL = list(SUPPORTED_LANGUAGE_DETAILS.keys())
|
|
SUPPORTED_LANGUAGE_ISO639_1_LOOKUP = {lang_details["iso 639-1"]: lang_name for lang_name, lang_details in SUPPORTED_LANGUAGE_DETAILS.items()}
|
|
|
|
# supported currencies ------------------------------------------------------------------------
|
|
SUPPORTED_CURRENCIES = ['€', '$']
|
|
|
|
# supported LLMs & settings -------------------------------------------------------------------
|
|
# SUPPORTED_EMBEDDINGS = ['openai.text-embedding-3-small', 'openai.text-embedding-3-large', 'mistral.mistral-embed']
|
|
SUPPORTED_EMBEDDINGS = ['mistral.mistral-embed']
|
|
SUPPORTED_LLMS = ['mistral.mistral-large-latest', 'mistral.mistral-medium_latest', 'mistral.mistral-small-latest']
|
|
|
|
# Annotation text chunk length
|
|
ANNOTATION_TEXT_CHUNK_LENGTH = 10000
|
|
|
|
# Environemnt Loaders
|
|
OPENAI_API_KEY = environ.get('OPENAI_API_KEY')
|
|
MISTRAL_API_KEY = environ.get('MISTRAL_API_KEY')
|
|
|
|
# Celery settings (see above for Redis settings) ----------------------------------------------
|
|
CELERY_TASK_SERIALIZER = 'json'
|
|
CELERY_RESULT_SERIALIZER = 'json'
|
|
CELERY_ACCEPT_CONTENT = ['json']
|
|
CELERY_TIMEZONE = 'UTC'
|
|
CELERY_ENABLE_UTC = True
|
|
|
|
# JWT settings --------------------------------------------------------------------------------
|
|
JWT_SECRET_KEY = environ.get('JWT_SECRET_KEY')
|
|
JWT_ACCESS_TOKEN_EXPIRES = timedelta(hours=1) # Set token expiry to 1 hour
|
|
JWT_ACCESS_TOKEN_EXPIRES_DEPLOY = timedelta(hours=24) # Set long-lived token for deployment
|
|
|
|
# API Encryption ------------------------------------------------------------------------------
|
|
API_ENCRYPTION_KEY = environ.get('API_ENCRYPTION_KEY')
|
|
|
|
# Email settings for API key notifications
|
|
PROMOTIONAL_IMAGE_URL = 'https://askeveai.com/wp-content/uploads/2024/07/Evie-Call-scaled.jpg' # Replace with your actual URL
|
|
|
|
# Langsmith settings
|
|
LANGCHAIN_TRACING_V2 = True
|
|
LANGCHAIN_ENDPOINT = 'https://api.smith.langchain.com'
|
|
LANGCHAIN_PROJECT = "eveai"
|
|
|
|
TENANT_TYPES = ['Active', 'Demo', 'Inactive', 'Test']
|
|
|
|
# The maximum number of seconds allowed for audio compression (to save resources)
|
|
MAX_COMPRESSION_DURATION = 60*10 # 10 minutes
|
|
# The maximum number of seconds allowed for transcribing audio
|
|
MAX_TRANSCRIPTION_DURATION = 60*10 # 10 minutes
|
|
# Maximum CPU usage for a compression task
|
|
COMPRESSION_CPU_LIMIT = 50
|
|
# Delay between compressing chunks in seconds
|
|
COMPRESSION_PROCESS_DELAY = 1
|
|
|
|
# WordPress Integration Settings
|
|
WORDPRESS_PROTOCOL = environ.get('WORDPRESS_PROTOCOL', 'http')
|
|
WORDPRESS_HOST = environ.get('WORDPRESS_HOST', 'host.docker.internal')
|
|
WORDPRESS_PORT = environ.get('WORDPRESS_PORT', '10003')
|
|
WORDPRESS_BASE_URL = f"{WORDPRESS_PROTOCOL}://{WORDPRESS_HOST}:{WORDPRESS_PORT}"
|
|
EXTERNAL_WORDPRESS_BASE_URL = 'localhost:10003'
|
|
|
|
# Prometheus PUSH Gataway
|
|
PUSH_GATEWAY_HOST = environ.get('PUSH_GATEWAY_HOST', 'pushgateway')
|
|
PUSH_GATEWAY_PORT = environ.get('PUSH_GATEWAY_PORT', '9091')
|
|
PUSH_GATEWAY_URL = f"{PUSH_GATEWAY_HOST}:{PUSH_GATEWAY_PORT}"
|
|
|
|
# Scaleway parameters
|
|
SW_EMAIL_ACCESS_KEY = environ.get('SW_EMAIL_ACCESS_KEY')
|
|
SW_EMAIL_SECRET_KEY = environ.get('SW_EMAIL_SECRET_KEY')
|
|
SW_EMAIL_SENDER = environ.get('SW_EMAIL_SENDER')
|
|
SW_EMAIL_NAME = environ.get('SW_EMAIL_NAME')
|
|
SW_PROJECT = environ.get('SW_PROJECT')
|
|
|
|
# Entitlement Constants
|
|
ENTITLEMENTS_MAX_PENDING_DAYS = 5 # Defines the maximum number of days a pending entitlement can be active
|
|
|
|
# Content Directory for static content like the changelog, terms & conditions, privacy statement, ...
|
|
CONTENT_DIR = '/app/content'
|
|
|
|
# Ensure health check endpoints are exempt from CSRF protection
|
|
SECURITY_EXEMPT_URLS = [
|
|
r'^/healthz($|/.*)',
|
|
r'^/_healthz($|/.*)',
|
|
]
|
|
SECURITY_LOGIN_WITHOUT_VIEWS = True # Dit voorkomt automatische redirects
|
|
|
|
|
|
class DevConfig(Config):
|
|
DEVELOPMENT = True
|
|
DEBUG = True
|
|
FLASK_DEBUG = True
|
|
EXPLAIN_TEMPLATE_LOADING = False
|
|
|
|
# Define the nginx prefix used for the specific apps
|
|
EVEAI_APP_LOCATION_PREFIX = ''
|
|
EVEAI_CHAT_LOCATION_PREFIX = '/chat'
|
|
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
|
|
|
# Define the static path
|
|
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
|
|
|
# PATH settings
|
|
ffmpeg_path = '/usr/bin/ffmpeg'
|
|
|
|
# OBJECT STORAGE
|
|
OBJECT_STORAGE_TYPE = 'MINIO'
|
|
OBJECT_STORAGE_TENANT_BASE = 'bucket'
|
|
# MINIO
|
|
MINIO_ENDPOINT = 'minio:9000'
|
|
MINIO_ACCESS_KEY = 'minioadmin'
|
|
MINIO_SECRET_KEY = 'minioadmin'
|
|
MINIO_USE_HTTPS = False
|
|
|
|
|
|
class StagingConfig(Config):
|
|
DEVELOPMENT = False
|
|
DEBUG = True
|
|
FLASK_DEBUG = True
|
|
EXPLAIN_TEMPLATE_LOADING = False
|
|
|
|
# Define the nginx prefix used for the specific apps
|
|
EVEAI_APP_LOCATION_PREFIX = ''
|
|
EVEAI_CHAT_LOCATION_PREFIX = ''
|
|
CHAT_CLIENT_PREFIX = ''
|
|
|
|
# Define the static path
|
|
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
|
|
|
# PATH settings
|
|
ffmpeg_path = '/usr/bin/ffmpeg'
|
|
|
|
# OBJECT STORAGE
|
|
OBJECT_STORAGE_TYPE = 'SCALEWAY'
|
|
OBJECT_STORAGE_TENANT_BASE = 'folder'
|
|
OBJECT_STORAGE_BUCKET_NAME = 'eveai-staging'
|
|
# MINIO
|
|
MINIO_ENDPOINT = environ.get('MINIO_ENDPOINT')
|
|
MINIO_ACCESS_KEY = environ.get('MINIO_ACCESS_KEY')
|
|
MINIO_SECRET_KEY = environ.get('MINIO_SECRET_KEY')
|
|
MINIO_USE_HTTPS = True
|
|
|
|
|
|
class ProdConfig(Config):
|
|
DEVELOPMENT = False
|
|
DEBUG = False
|
|
FLASK_DEBUG = False
|
|
EXPLAIN_TEMPLATE_LOADING = False
|
|
|
|
# SESSION SETTINGS
|
|
SESSION_COOKIE_SECURE = True
|
|
|
|
WTF_CSRF_SSL_STRICT = True # Set to True if using HTTPS
|
|
|
|
# Define the nginx prefix used for the specific apps
|
|
EVEAI_APP_LOCATION_PREFIX = ''
|
|
EVEAI_CHAT_LOCATION_PREFIX = ''
|
|
|
|
# Define the static path
|
|
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
|
|
|
# PATH settings
|
|
ffmpeg_path = '/usr/bin/ffmpeg'
|
|
|
|
# MINIO
|
|
MINIO_ENDPOINT = environ.get('MINIO_ENDPOINT')
|
|
MINIO_ACCESS_KEY = environ.get('MINIO_ACCESS_KEY')
|
|
MINIO_SECRET_KEY = environ.get('MINIO_SECRET_KEY')
|
|
MINIO_USE_HTTPS = True
|
|
|
|
|
|
def get_config(config_name='dev'):
|
|
configs = {
|
|
'dev': DevConfig,
|
|
'staging': StagingConfig,
|
|
'prod': ProdConfig,
|
|
'default': DevConfig,
|
|
}
|
|
return configs.get(config_name)
|