- error handling now uses a more comprehensive error communication system.
This commit is contained in:
22
common/templates/error/401.html
Normal file
22
common/templates/error/401.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Unauthorized</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background:#f7f7f9; color:#222; }
|
||||||
|
.wrap { max-width: 720px; margin: 10vh auto; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:32px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
|
||||||
|
h1 { margin: 0 0 8px; font-size: 28px; }
|
||||||
|
p { margin: 0 0 16px; line-height:1.6; }
|
||||||
|
a.btn { display:inline-block; padding:10px 16px; background:#2c3e50; color:#fff; text-decoration:none; border-radius:8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrap">
|
||||||
|
<h1>Not authorized</h1>
|
||||||
|
<p>Your session may have expired or this action is not permitted.</p>
|
||||||
|
<p><a class="btn" href="/">Go to home</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
common/templates/error/403.html
Normal file
22
common/templates/error/403.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Forbidden</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background:#f7f7f9; color:#222; }
|
||||||
|
.wrap { max-width: 720px; margin: 10vh auto; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:32px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
|
||||||
|
h1 { margin: 0 0 8px; font-size: 28px; }
|
||||||
|
p { margin: 0 0 16px; line-height:1.6; }
|
||||||
|
a.btn { display:inline-block; padding:10px 16px; background:#2c3e50; color:#fff; text-decoration:none; border-radius:8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrap">
|
||||||
|
<h1>Access forbidden</h1>
|
||||||
|
<p>You don't have permission to access this resource.</p>
|
||||||
|
<p><a class="btn" href="/">Go to home</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
common/templates/error/404.html
Normal file
22
common/templates/error/404.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Page not found</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background:#f7f7f9; color:#222; }
|
||||||
|
.wrap { max-width: 720px; margin: 10vh auto; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:32px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
|
||||||
|
h1 { margin: 0 0 8px; font-size: 28px; }
|
||||||
|
p { margin: 0 0 16px; line-height:1.6; }
|
||||||
|
a.btn { display:inline-block; padding:10px 16px; background:#2c3e50; color:#fff; text-decoration:none; border-radius:8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrap">
|
||||||
|
<h1>Page not found</h1>
|
||||||
|
<p>The page you are looking for doesn’t exist or has been moved.</p>
|
||||||
|
<p><a class="btn" href="/">Go to home</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
common/templates/error/500.html
Normal file
22
common/templates/error/500.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Something went wrong</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background:#f7f7f9; color:#222; }
|
||||||
|
.wrap { max-width: 720px; margin: 10vh auto; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:32px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
|
||||||
|
h1 { margin: 0 0 8px; font-size: 28px; }
|
||||||
|
p { margin: 0 0 16px; line-height:1.6; }
|
||||||
|
a.btn { display:inline-block; padding:10px 16px; background:#2c3e50; color:#fff; text-decoration:none; border-radius:8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrap">
|
||||||
|
<h1>We’re sorry — something went wrong</h1>
|
||||||
|
<p>Please try again later. If the issue persists, contact support.</p>
|
||||||
|
<p><a class="btn" href="/">Go to home</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
22
common/templates/error/generic.html
Normal file
22
common/templates/error/generic.html
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Error</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; background:#f7f7f9; color:#222; }
|
||||||
|
.wrap { max-width: 720px; margin: 10vh auto; background:#fff; border:1px solid #e5e7eb; border-radius:12px; padding:32px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); }
|
||||||
|
h1 { margin: 0 0 8px; font-size: 28px; }
|
||||||
|
p { margin: 0 0 16px; line-height:1.6; }
|
||||||
|
a.btn { display:inline-block; padding:10px 16px; background:#2c3e50; color:#fff; text-decoration:none; border-radius:8px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<main class="wrap">
|
||||||
|
<h1>Oops! Something went wrong</h1>
|
||||||
|
<p>Please try again. If the issue persists, contact support.</p>
|
||||||
|
<p><a class="btn" href="/">Go to home</a></p>
|
||||||
|
</main>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -10,41 +10,54 @@ from common.utils.nginx_utils import prefixed_url_for
|
|||||||
|
|
||||||
|
|
||||||
def not_found_error(error):
|
def not_found_error(error):
|
||||||
if not current_user.is_authenticated:
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
return redirect(prefixed_url_for('security.login'))
|
if profile == 'web_app':
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
current_app.logger.error(f"Not Found Error: {error}")
|
current_app.logger.error(f"Not Found Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error/404.html'), 404
|
return render_template('error/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
def internal_server_error(error):
|
def internal_server_error(error):
|
||||||
if not current_user.is_authenticated:
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
return redirect(prefixed_url_for('security.login'))
|
if profile == 'web_app':
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
current_app.logger.error(f"Internal Server Error: {error}")
|
current_app.logger.error(f"Internal Server Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error/500.html'), 500
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
def not_authorised_error(error):
|
def not_authorised_error(error):
|
||||||
if not current_user.is_authenticated:
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
return redirect(prefixed_url_for('security.login'))
|
if profile == 'web_app':
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
current_app.logger.error(f"Not Authorised Error: {error}")
|
current_app.logger.error(f"Not Authorised Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error/401.html')
|
return render_template('error/401.html'), 401
|
||||||
|
|
||||||
|
|
||||||
def access_forbidden(error):
|
def access_forbidden(error):
|
||||||
if not current_user.is_authenticated:
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
return redirect(prefixed_url_for('security.login'))
|
if profile == 'web_app':
|
||||||
|
if not current_user.is_authenticated:
|
||||||
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
current_app.logger.error(f"Access Forbidden: {error}")
|
current_app.logger.error(f"Access Forbidden: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error/403.html')
|
return render_template('error/403.html'), 403
|
||||||
|
|
||||||
|
|
||||||
def key_error_handler(error):
|
def key_error_handler(error):
|
||||||
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
# Check if the KeyError is specifically for 'tenant'
|
# Check if the KeyError is specifically for 'tenant'
|
||||||
if str(error) == "'tenant'":
|
if str(error) == "'tenant'":
|
||||||
return redirect(prefixed_url_for('security.login'))
|
if profile == 'web_app':
|
||||||
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
|
else:
|
||||||
|
current_app.logger.warning("Session tenant missing in chat_client context")
|
||||||
|
return render_template('error/401.html'), 401
|
||||||
# For other KeyErrors, you might want to log the error and return a generic error page
|
# For other KeyErrors, you might want to log the error and return a generic error page
|
||||||
current_app.logger.error(f"Key Error: {error}")
|
current_app.logger.error(f"Key Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
@@ -79,19 +92,24 @@ def no_tenant_selected_error(error):
|
|||||||
"""Handle errors when no tenant is selected in the current session.
|
"""Handle errors when no tenant is selected in the current session.
|
||||||
|
|
||||||
This typically happens when a session expires or becomes invalid after
|
This typically happens when a session expires or becomes invalid after
|
||||||
a long period of inactivity. The user will be redirected to the login page.
|
a long period of inactivity. The user will be redirected to the login page (web_app)
|
||||||
|
or shown an error page (chat_client).
|
||||||
"""
|
"""
|
||||||
|
profile = current_app.config.get('ERRORS_PROFILE', 'web_app')
|
||||||
current_app.logger.error(f"No Session Tenant Error: {error}")
|
current_app.logger.error(f"No Session Tenant Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
flash('Your session expired. You will have to re-enter your credentials', 'warning')
|
flash('Your session expired. You will have to re-enter your credentials', 'warning')
|
||||||
|
|
||||||
# Perform logout if user is authenticated
|
if profile == 'web_app':
|
||||||
if current_user.is_authenticated:
|
# Perform logout if user is authenticated
|
||||||
from flask_security.utils import logout_user
|
if current_user.is_authenticated:
|
||||||
logout_user()
|
from flask_security.utils import logout_user
|
||||||
|
logout_user()
|
||||||
# Redirect to login page
|
# Redirect to login page
|
||||||
return redirect(prefixed_url_for('security.login'))
|
return redirect(prefixed_url_for('security.login', for_redirect=True))
|
||||||
|
else:
|
||||||
|
# chat_client: render 401 page
|
||||||
|
return render_template('error/401.html'), 401
|
||||||
|
|
||||||
|
|
||||||
def general_exception(e):
|
def general_exception(e):
|
||||||
@@ -122,7 +140,10 @@ def template_syntax_error(error):
|
|||||||
error_details=f"Error in template '{error.filename}' at line {error.lineno}: {error.message}"), 500
|
error_details=f"Error in template '{error.filename}' at line {error.lineno}: {error.message}"), 500
|
||||||
|
|
||||||
|
|
||||||
def register_error_handlers(app):
|
def register_error_handlers(app, profile: str = 'web_app'):
|
||||||
|
# Store profile in app config to drive handler behavior
|
||||||
|
app.config['ERRORS_PROFILE'] = profile
|
||||||
|
|
||||||
app.register_error_handler(404, not_found_error)
|
app.register_error_handler(404, not_found_error)
|
||||||
app.register_error_handler(500, internal_server_error)
|
app.register_error_handler(500, internal_server_error)
|
||||||
app.register_error_handler(401, not_authorised_error)
|
app.register_error_handler(401, not_authorised_error)
|
||||||
|
|||||||
@@ -363,8 +363,6 @@ class DevConfig(Config):
|
|||||||
EXPLAIN_TEMPLATE_LOADING = False
|
EXPLAIN_TEMPLATE_LOADING = False
|
||||||
|
|
||||||
# Define the nginx prefix used for the specific apps
|
# Define the nginx prefix used for the specific apps
|
||||||
EVEAI_APP_LOCATION_PREFIX = ''
|
|
||||||
EVEAI_CHAT_LOCATION_PREFIX = '/chat'
|
|
||||||
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
||||||
|
|
||||||
# Define the static path
|
# Define the static path
|
||||||
@@ -391,8 +389,6 @@ class TestConfig(Config):
|
|||||||
EXPLAIN_TEMPLATE_LOADING = False
|
EXPLAIN_TEMPLATE_LOADING = False
|
||||||
|
|
||||||
# Define the nginx prefix used for the specific apps
|
# Define the nginx prefix used for the specific apps
|
||||||
EVEAI_APP_LOCATION_PREFIX = ''
|
|
||||||
EVEAI_CHAT_LOCATION_PREFIX = '/chat'
|
|
||||||
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
||||||
|
|
||||||
# Define the static path
|
# Define the static path
|
||||||
@@ -419,9 +415,7 @@ class StagingConfig(Config):
|
|||||||
EXPLAIN_TEMPLATE_LOADING = False
|
EXPLAIN_TEMPLATE_LOADING = False
|
||||||
|
|
||||||
# Define the nginx prefix used for the specific apps
|
# Define the nginx prefix used for the specific apps
|
||||||
EVEAI_APP_LOCATION_PREFIX = ''
|
CHAT_CLIENT_PREFIX = 'chat-client/chat/'
|
||||||
EVEAI_CHAT_LOCATION_PREFIX = ''
|
|
||||||
CHAT_CLIENT_PREFIX = ''
|
|
||||||
|
|
||||||
# Define the static path
|
# Define the static path
|
||||||
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
||||||
@@ -452,11 +446,10 @@ class ProdConfig(Config):
|
|||||||
WTF_CSRF_SSL_STRICT = True # Set to True if using HTTPS
|
WTF_CSRF_SSL_STRICT = True # Set to True if using HTTPS
|
||||||
|
|
||||||
# Define the nginx prefix used for the specific apps
|
# Define the nginx prefix used for the specific apps
|
||||||
EVEAI_APP_LOCATION_PREFIX = ''
|
EVEAI_CHAT_LOCATION_PREFIX = 'EVEAI_APP_LOCATION_PREFIX'
|
||||||
EVEAI_CHAT_LOCATION_PREFIX = ''
|
|
||||||
|
|
||||||
# Define the static path
|
# Define the static path
|
||||||
STATIC_URL = 'https://evie-staging-static.askeveai.com'
|
STATIC_URL = 'https://evie-prod-static.askeveai.com'
|
||||||
|
|
||||||
# PATH settings
|
# PATH settings
|
||||||
ffmpeg_path = '/usr/bin/ffmpeg'
|
ffmpeg_path = '/usr/bin/ffmpeg'
|
||||||
|
|||||||
@@ -1,18 +1,20 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from flask import Flask, jsonify, request, url_for
|
from flask import Flask, jsonify, request, url_for, session as flask_session
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
import logging.config
|
import logging.config
|
||||||
|
from jinja2 import ChoiceLoader, FileSystemLoader
|
||||||
|
|
||||||
from common.extensions import (db, bootstrap, cors, csrf, session,
|
from common.extensions import (db, bootstrap, cors, csrf, session,
|
||||||
minio_client, simple_encryption, metrics, cache_manager, content_manager)
|
minio_client, simple_encryption, metrics, cache_manager, content_manager)
|
||||||
from common.models.user import Tenant, SpecialistMagicLinkTenant
|
from common.models.user import Tenant, SpecialistMagicLinkTenant
|
||||||
from config.logging_config import configure_logging
|
from config.logging_config import configure_logging
|
||||||
from eveai_chat_client.utils.errors import register_error_handlers
|
from common.utils.errors import register_error_handlers
|
||||||
from common.utils.celery_utils import make_celery, init_celery
|
from common.utils.celery_utils import make_celery, init_celery
|
||||||
from common.utils.template_filters import register_filters
|
from common.utils.template_filters import register_filters
|
||||||
from config.config import get_config
|
from config.config import get_config
|
||||||
|
from common.utils.chat_utils import get_default_chat_customisation
|
||||||
|
|
||||||
|
|
||||||
def create_app(config_file=None):
|
def create_app(config_file=None):
|
||||||
@@ -57,11 +59,23 @@ def create_app(config_file=None):
|
|||||||
app.celery = make_celery(app.name, app.config)
|
app.celery = make_celery(app.name, app.config)
|
||||||
init_celery(app.celery, app)
|
init_celery(app.celery, app)
|
||||||
|
|
||||||
|
# Configure template loader with fallback to common/templates
|
||||||
|
try:
|
||||||
|
import os as _os
|
||||||
|
common_templates_path = _os.path.normpath(_os.path.join(app.root_path, '..', 'common', 'templates'))
|
||||||
|
app.jinja_loader = ChoiceLoader([
|
||||||
|
app.jinja_loader,
|
||||||
|
FileSystemLoader(common_templates_path),
|
||||||
|
])
|
||||||
|
app.logger.debug(f"Added common templates path: {common_templates_path}")
|
||||||
|
except Exception as e:
|
||||||
|
app.logger.error(f"Failed to configure ChoiceLoader for common templates: {e}")
|
||||||
|
|
||||||
# Register Blueprints
|
# Register Blueprints
|
||||||
register_blueprints(app)
|
register_blueprints(app)
|
||||||
|
|
||||||
# Register Error Handlers
|
# Register Error Handlers (shared, profile-aware)
|
||||||
register_error_handlers(app)
|
register_error_handlers(app, profile='chat_client')
|
||||||
|
|
||||||
# Register Cache Handlers
|
# Register Cache Handlers
|
||||||
register_cache_handlers(app)
|
register_cache_handlers(app)
|
||||||
@@ -85,6 +99,19 @@ def create_app(config_file=None):
|
|||||||
# Register template filters
|
# Register template filters
|
||||||
register_filters(app)
|
register_filters(app)
|
||||||
|
|
||||||
|
# Always inject chat customisation for templates (safe defaults)
|
||||||
|
@app.context_processor
|
||||||
|
def inject_customisation():
|
||||||
|
try:
|
||||||
|
tm = flask_session.get('tenant_make')
|
||||||
|
options = None
|
||||||
|
if tm and isinstance(tm, dict):
|
||||||
|
options = tm.get('chat_customisation_options')
|
||||||
|
customisation = get_default_chat_customisation(options)
|
||||||
|
except Exception:
|
||||||
|
customisation = get_default_chat_customisation(None)
|
||||||
|
return {'customisation': customisation}
|
||||||
|
|
||||||
app.logger.info(f"EveAI Chat Client Started Successfully (PID: {os.getpid()})")
|
app.logger.info(f"EveAI Chat Client Started Successfully (PID: {os.getpid()})")
|
||||||
app.logger.info("-------------------------------------------------------------------------------------------------")
|
app.logger.info("-------------------------------------------------------------------------------------------------")
|
||||||
|
|
||||||
@@ -115,8 +142,7 @@ def register_extensions(app):
|
|||||||
def register_blueprints(app):
|
def register_blueprints(app):
|
||||||
from .views.chat_views import chat_bp
|
from .views.chat_views import chat_bp
|
||||||
app.register_blueprint(chat_bp)
|
app.register_blueprint(chat_bp)
|
||||||
from .views.error_views import error_bp
|
# Do not register local error blueprint; use shared error handlers from common
|
||||||
app.register_blueprint(error_bp)
|
|
||||||
from .views.healthz_views import healthz_bp
|
from .views.healthz_views import healthz_bp
|
||||||
app.register_blueprint(healthz_bp)
|
app.register_blueprint(healthz_bp)
|
||||||
|
|
||||||
|
|||||||
@@ -9,31 +9,31 @@ from common.utils.eveai_exceptions import EveAINoSessionTenant
|
|||||||
def not_found_error(error):
|
def not_found_error(error):
|
||||||
current_app.logger.error(f"Not Found Error: {error}")
|
current_app.logger.error(f"Not Found Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="Page not found."), 404
|
return render_template('error/404.html'), 404
|
||||||
|
|
||||||
|
|
||||||
def internal_server_error(error):
|
def internal_server_error(error):
|
||||||
current_app.logger.error(f"Internal Server Error: {error}")
|
current_app.logger.error(f"Internal Server Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="Internal server error."), 500
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
def not_authorised_error(error):
|
def not_authorised_error(error):
|
||||||
current_app.logger.error(f"Not Authorised Error: {error}")
|
current_app.logger.error(f"Not Authorised Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="Not authorized."), 401
|
return render_template('error/401.html'), 401
|
||||||
|
|
||||||
|
|
||||||
def access_forbidden(error):
|
def access_forbidden(error):
|
||||||
current_app.logger.error(f"Access Forbidden: {error}")
|
current_app.logger.error(f"Access Forbidden: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="Access forbidden."), 403
|
return render_template('error/403.html'), 403
|
||||||
|
|
||||||
|
|
||||||
def key_error_handler(error):
|
def key_error_handler(error):
|
||||||
current_app.logger.error(f"Key Error: {error}")
|
current_app.logger.error(f"Key Error: {error}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="An unexpected error occurred."), 500
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
def attribute_error_handler(error):
|
def attribute_error_handler(error):
|
||||||
@@ -41,7 +41,7 @@ def attribute_error_handler(error):
|
|||||||
error_msg = str(error)
|
error_msg = str(error)
|
||||||
current_app.logger.error(f"AttributeError: {error_msg}")
|
current_app.logger.error(f"AttributeError: {error_msg}")
|
||||||
current_app.logger.error(traceback.format_exc())
|
current_app.logger.error(traceback.format_exc())
|
||||||
return render_template('error.html', message="An application error occurred."), 500
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
def no_tenant_selected_error(error):
|
def no_tenant_selected_error(error):
|
||||||
|
|||||||
@@ -37,9 +37,7 @@ def log_after_request(response):
|
|||||||
|
|
||||||
@chat_bp.route('/')
|
@chat_bp.route('/')
|
||||||
def index():
|
def index():
|
||||||
customisation = get_default_chat_customisation()
|
return render_template('error/404.html'), 404
|
||||||
return render_template('error.html', message="Please use a valid magic link to access the chat.",
|
|
||||||
customisation=customisation)
|
|
||||||
|
|
||||||
|
|
||||||
@chat_bp.route('/<magic_link_code>')
|
@chat_bp.route('/<magic_link_code>')
|
||||||
@@ -53,14 +51,14 @@ def chat(magic_link_code):
|
|||||||
|
|
||||||
if not magic_link_tenant:
|
if not magic_link_tenant:
|
||||||
current_app.logger.error(f"Invalid magic link code: {magic_link_code}")
|
current_app.logger.error(f"Invalid magic link code: {magic_link_code}")
|
||||||
return render_template('error.html', message="Invalid magic link code.")
|
return render_template('error/404.html'), 404
|
||||||
|
|
||||||
# Get tenant information
|
# Get tenant information
|
||||||
tenant_id = magic_link_tenant.tenant_id
|
tenant_id = magic_link_tenant.tenant_id
|
||||||
tenant = Tenant.query.get(tenant_id)
|
tenant = Tenant.query.get(tenant_id)
|
||||||
if not tenant:
|
if not tenant:
|
||||||
current_app.logger.error(f"Tenant not found for ID: {tenant_id}")
|
current_app.logger.error(f"Tenant not found for ID: {tenant_id}")
|
||||||
return render_template('error.html', message="Tenant not found.")
|
return render_template('error/404.html'), 404
|
||||||
# Switch to tenant schema
|
# Switch to tenant schema
|
||||||
Database(tenant_id).switch_schema()
|
Database(tenant_id).switch_schema()
|
||||||
|
|
||||||
@@ -68,19 +66,19 @@ def chat(magic_link_code):
|
|||||||
specialist_ml = SpecialistMagicLink.query.filter_by(magic_link_code=magic_link_code).first()
|
specialist_ml = SpecialistMagicLink.query.filter_by(magic_link_code=magic_link_code).first()
|
||||||
if not specialist_ml:
|
if not specialist_ml:
|
||||||
current_app.logger.error(f"Specialist magic link not found in tenant schema: {tenant_id}")
|
current_app.logger.error(f"Specialist magic link not found in tenant schema: {tenant_id}")
|
||||||
return render_template('error.html', message="Specialist configuration not found.")
|
return render_template('error/404.html'), 404
|
||||||
|
|
||||||
# Get relevant TenantMake
|
# Get relevant TenantMake
|
||||||
tenant_make = TenantMake.query.get(specialist_ml.tenant_make_id)
|
tenant_make = TenantMake.query.get(specialist_ml.tenant_make_id)
|
||||||
if not tenant_make:
|
if not tenant_make:
|
||||||
current_app.logger.error(f"Tenant make not found: {specialist_ml.tenant_make_id}")
|
current_app.logger.error(f"Tenant make not found: {specialist_ml.tenant_make_id}")
|
||||||
return render_template('error.html', message="Tenant make not found.")
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
# Get specialist details
|
# Get specialist details
|
||||||
specialist = Specialist.query.get(specialist_ml.specialist_id)
|
specialist = Specialist.query.get(specialist_ml.specialist_id)
|
||||||
if not specialist:
|
if not specialist:
|
||||||
current_app.logger.error(f"Specialist not found: {specialist_ml.specialist_id}")
|
current_app.logger.error(f"Specialist not found: {specialist_ml.specialist_id}")
|
||||||
return render_template('error.html', message="Specialist not found.")
|
return render_template('error/404.html'), 404
|
||||||
|
|
||||||
# Store necessary information in session
|
# Store necessary information in session
|
||||||
session['tenant'] = tenant.to_dict()
|
session['tenant'] = tenant.to_dict()
|
||||||
@@ -124,7 +122,7 @@ def chat(magic_link_code):
|
|||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f"Error in chat view: {str(e)}", exc_info=True)
|
current_app.logger.error(f"Error in chat view: {str(e)}", exc_info=True)
|
||||||
return render_template('error.html', message="An error occurred while setting up the chat.")
|
return render_template('error/500.html'), 500
|
||||||
|
|
||||||
|
|
||||||
@chat_bp.route('/api/send_message', methods=['POST'])
|
@chat_bp.route('/api/send_message', methods=['POST'])
|
||||||
|
|||||||
@@ -28,18 +28,19 @@ def create_app(config_file=None):
|
|||||||
|
|
||||||
configure_logging()
|
configure_logging()
|
||||||
|
|
||||||
app.logger.info('Starting up eveai_chat_workers...')
|
|
||||||
register_extensions(app)
|
register_extensions(app)
|
||||||
|
|
||||||
|
register_cache_handlers(app)
|
||||||
|
|
||||||
from . import specialists, retrievers
|
from . import specialists, retrievers
|
||||||
|
|
||||||
celery = make_celery(app.name, app.config)
|
celery = make_celery(app.name, app.config)
|
||||||
init_celery(celery, app)
|
init_celery(celery, app)
|
||||||
|
|
||||||
register_cache_handlers(app)
|
from . import tasks
|
||||||
|
|
||||||
from eveai_chat_workers import tasks
|
app.logger.info("EveAI Worker Server Started Successfully")
|
||||||
print(tasks.tasks_ping())
|
app.logger.info("-------------------------------------------------------------------------------------------------")
|
||||||
|
|
||||||
return app, celery
|
return app, celery
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import traceback
|
|||||||
from flask import current_app
|
from flask import current_app
|
||||||
from celery import states
|
from celery import states
|
||||||
from sqlalchemy.exc import SQLAlchemyError, InterfaceError, OperationalError
|
from sqlalchemy.exc import SQLAlchemyError, InterfaceError, OperationalError
|
||||||
|
from redis.exceptions import ConnectionError as RedisConnectionError, TimeoutError as RedisTimeoutError
|
||||||
|
|
||||||
from common.utils.config_field_types import TaggingFields
|
from common.utils.config_field_types import TaggingFields
|
||||||
from common.utils.database import Database
|
from common.utils.database import Database
|
||||||
@@ -21,7 +22,9 @@ from common.utils.execution_progress import ExecutionProgressTracker
|
|||||||
|
|
||||||
|
|
||||||
# Healthcheck task
|
# Healthcheck task
|
||||||
@current_celery.task(name='ping', queue='llm_interactions')
|
@current_celery.task(bind=True, name='ping', queue='llm_interactions',
|
||||||
|
autoretry_for=(InterfaceError, OperationalError, RedisConnectionError, RedisTimeoutError, OSError),
|
||||||
|
retry_backoff=True, retry_jitter=True, max_retries=5)
|
||||||
def ping():
|
def ping():
|
||||||
return 'pong'
|
return 'pong'
|
||||||
|
|
||||||
@@ -215,7 +218,9 @@ def prepare_arguments(specialist: Any, arguments: Dict[str, Any]) -> Dict[str, A
|
|||||||
raise ArgumentPreparationError(str(e))
|
raise ArgumentPreparationError(str(e))
|
||||||
|
|
||||||
|
|
||||||
@current_celery.task(bind=True, name='execute_specialist', queue='llm_interactions', autoretry_for=(InterfaceError, OperationalError), retry_backoff=True, retry_jitter=True, max_retries=5)
|
@current_celery.task(bind=True, name='execute_specialist', queue='llm_interactions',
|
||||||
|
autoretry_for=(InterfaceError, OperationalError),
|
||||||
|
retry_backoff=True, retry_jitter=True, max_retries=5)
|
||||||
def execute_specialist(self, tenant_id: int, specialist_id: int, arguments: Dict[str, Any],
|
def execute_specialist(self, tenant_id: int, specialist_id: int, arguments: Dict[str, Any],
|
||||||
session_id: str, user_timezone: str) -> dict:
|
session_id: str, user_timezone: str) -> dict:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from langchain_core.prompts import ChatPromptTemplate
|
|||||||
from langchain_core.runnables import RunnablePassthrough
|
from langchain_core.runnables import RunnablePassthrough
|
||||||
from sqlalchemy import or_
|
from sqlalchemy import or_
|
||||||
from sqlalchemy.exc import SQLAlchemyError, InterfaceError, OperationalError
|
from sqlalchemy.exc import SQLAlchemyError, InterfaceError, OperationalError
|
||||||
|
from redis.exceptions import ConnectionError as RedisConnectionError, TimeoutError as RedisTimeoutError
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
from common.extensions import db, cache_manager
|
from common.extensions import db, cache_manager
|
||||||
@@ -32,13 +33,15 @@ from common.utils.config_field_types import json_to_pattern_list
|
|||||||
|
|
||||||
|
|
||||||
# Healthcheck task
|
# Healthcheck task
|
||||||
@current_celery.task(name='ping', queue='embeddings')
|
@current_celery.task(bind=True, name='ping', queue='embeddings',
|
||||||
|
autoretry_for=(InterfaceError, OperationalError, RedisConnectionError, RedisTimeoutError, OSError),
|
||||||
|
retry_backoff=True, retry_jitter=True, max_retries=5)
|
||||||
def ping():
|
def ping():
|
||||||
return 'pong'
|
return 'pong'
|
||||||
|
|
||||||
|
|
||||||
@current_celery.task(bind=True, name='create_embeddings', queue='embeddings',
|
@current_celery.task(bind=True, name='create_embeddings', queue='embeddings',
|
||||||
autoretry_for=(InterfaceError, OperationalError),
|
autoretry_for=(InterfaceError, OperationalError, RedisConnectionError, RedisTimeoutError, OSError),
|
||||||
retry_backoff=True, retry_jitter=True, max_retries=5)
|
retry_backoff=True, retry_jitter=True, max_retries=5)
|
||||||
def create_embeddings(self, tenant_id, document_version_id):
|
def create_embeddings(self, tenant_id, document_version_id):
|
||||||
document_version = None
|
document_version = None
|
||||||
|
|||||||
Reference in New Issue
Block a user