Files
eveAI/config/config.py
Josako a9bbd1f466 - Ensure prefix is passed for all services
- 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
2025-09-09 08:45:45 +02:00

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)