- Corrections to tenant, catalog, and tenant_make

- Clean-up of tenant elements
- ensure the chat_client get's it's initial call rifht.
This commit is contained in:
Josako
2025-06-10 16:10:08 +02:00
parent 3f77871c4f
commit 9cc266b97f
13 changed files with 182 additions and 38 deletions

View File

@@ -45,6 +45,21 @@ class Specialist(db.Model):
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
updated_by = db.Column(db.Integer, db.ForeignKey(User.id)) updated_by = db.Column(db.Integer, db.ForeignKey(User.id))
def __repr__(self):
return f"<Specialist {self.id}: {self.name}>"
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'description': self.description,
'type': self.type,
'type_version': self.type_version,
'configuration': self.configuration,
'arguments': self.arguments,
'active': self.active,
}
class EveAIAsset(db.Model): class EveAIAsset(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
@@ -238,3 +253,14 @@ class SpecialistMagicLink(db.Model):
def __repr__(self): def __repr__(self):
return f"<SpecialistMagicLink {self.specialist_id} {self.magic_link_code}>" return f"<SpecialistMagicLink {self.specialist_id} {self.magic_link_code}>"
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'description': self.description,
'magic_link_code': self.magic_link_code,
'valid_from': self.valid_from,
'valid_to': self.valid_to,
'specialist_args': self.specialist_args,
}

View File

@@ -28,7 +28,6 @@ class Tenant(db.Model):
# language information # language information
default_language = db.Column(db.String(2), nullable=True) default_language = db.Column(db.String(2), nullable=True)
allowed_languages = db.Column(ARRAY(sa.String(2)), nullable=True)
# Entitlements # Entitlements
currency = db.Column(db.String(20), nullable=True) currency = db.Column(db.String(20), nullable=True)
@@ -63,7 +62,6 @@ class Tenant(db.Model):
'timezone': self.timezone, 'timezone': self.timezone,
'type': self.type, 'type': self.type,
'default_language': self.default_language, 'default_language': self.default_language,
'allowed_languages': self.allowed_languages,
'currency': self.currency, 'currency': self.currency,
'default_tenant_make_id': self.default_tenant_make_id, 'default_tenant_make_id': self.default_tenant_make_id,
} }
@@ -198,6 +196,20 @@ class TenantMake(db.Model):
updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now()) updated_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now(), onupdate=db.func.now())
updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id')) updated_by = db.Column(db.Integer, db.ForeignKey('public.user.id'))
def __repr__(self):
return f"<TenantMake {self.id} for tenant {self.tenant_id}: {self.name}>"
def to_dict(self):
return {
'id': self.id,
'name': self.name,
'description': self.description,
'active': self.active,
'website': self.website,
'logo_url': self.logo_url,
'chat_customisation_options': self.chat_customisation_options,
}
class Partner(db.Model): class Partner(db.Model):
__bind_key__ = 'public' __bind_key__ = 'public'

View File

@@ -220,3 +220,17 @@ class SpecialistServices:
db.session.add(tool) db.session.add(tool)
current_app.logger.info(f"Created tool {tool.id} of type {tool_type}") current_app.logger.info(f"Created tool {tool.id} of type {tool_type}")
return tool return tool
@staticmethod
def get_specialist_system_field(specialist_id, config_name, system_name):
specialist = Specialist.query.get(specialist_id)
if not specialist:
raise ValueError(f"Specialist with ID {specialist_id} not found")
config = cache_manager.specialists_config_cache.get_config(specialist.type, specialist.version)
if not config:
raise ValueError(f"No configuration found for {specialist.type} version {specialist.version}")
potential_field = config.get(config_name, None)
if potential_field:
if potential_field.type == 'system' and potential_field.system_name == system_name:
return specialist.configuration.get(config_name, None)
return None

View File

@@ -5,6 +5,17 @@ All notable changes to EveAI will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.3.4-alfa]
### Added
- Introduction of Tenant Make
- Introduction of 'system' type for dynamic attributes
- Introduce Tenant Make to Traicie Specialists
### Changed
- Enable Specialist 'activation' / 'deactivation'
- Unique constraints introduced for Catalog Name (tenant level) and make name (public level)
## [2.3.3-alfa] ## [2.3.3-alfa]
### Added ### Added

View File

@@ -8,7 +8,19 @@
{% endmacro %} {% endmacro %}
{% macro render_field_content(field, disabled=False, readonly=False, class='') %} {% macro render_field_content(field, disabled=False, readonly=False, class='') %}
{% if field.type == 'BooleanField' %} {# Check if this is a hidden input field, if so, render only the field without label #}
{{ debug_to_console("Field Class: ", field.widget.__class__.__name__) }}
{% if field.widget.__class__.__name__ == 'HiddenInput' %}
{{ debug_to_console("Hidden Field: ", "Detected") }}
{{ field(class="form-control " + class, disabled=disabled, readonly=readonly) }}
{% if field.errors %}
<div class="invalid-feedback d-block">
{% for error in field.errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% elif field.type == 'BooleanField' %}
<div class="form-group"> <div class="form-group">
<div class="form-check form-switch"> <div class="form-check form-switch">
{{ field(class="form-check-input " + class, disabled=disabled, readonly=readonly, required=False) }} {{ field(class="form-check-input " + class, disabled=disabled, readonly=readonly, required=False) }}

View File

@@ -26,7 +26,6 @@ def validate_catalog_name(form, field):
class CatalogForm(FlaskForm): class CatalogForm(FlaskForm):
id = IntegerField('ID', widget=HiddenInput())
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_catalog_name]) name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_catalog_name])
description = TextAreaField('Description', validators=[Optional()]) description = TextAreaField('Description', validators=[Optional()])

View File

@@ -141,7 +141,6 @@ class SpecialistMagicLinkForm(FlaskForm):
description = TextAreaField('Description', validators=[Optional()]) description = TextAreaField('Description', validators=[Optional()])
magic_link_code = StringField('Magic Link Code', validators=[DataRequired(), Length(max=55)], render_kw={'readonly': True}) magic_link_code = StringField('Magic Link Code', validators=[DataRequired(), Length(max=55)], render_kw={'readonly': True})
specialist_id = SelectField('Specialist', validators=[DataRequired()]) specialist_id = SelectField('Specialist', validators=[DataRequired()])
tenant_make_id = SelectField('Tenant Make', validators=[Optional()], coerce=int)
valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()]) valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()])
valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()]) valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()])
@@ -155,10 +154,6 @@ class SpecialistMagicLinkForm(FlaskForm):
# Dynamically populate the specialist field # Dynamically populate the specialist field
self.specialist_id.choices = [(specialist.id, specialist.name) for specialist in specialists] self.specialist_id.choices = [(specialist.id, specialist.name) for specialist in specialists]
# Dynamically populate the tenant_make field with None as first option
tenant_makes = TenantMake.query.all()
self.tenant_make_id.choices = [(0, 'None')] + [(make.id, make.name) for make in tenant_makes]
class EditSpecialistMagicLinkForm(DynamicFormBase): class EditSpecialistMagicLinkForm(DynamicFormBase):
name = StringField('Name', validators=[DataRequired(), Length(max=50)]) name = StringField('Name', validators=[DataRequired(), Length(max=50)])
@@ -168,7 +163,6 @@ class EditSpecialistMagicLinkForm(DynamicFormBase):
specialist_id = IntegerField('Specialist', validators=[DataRequired()], render_kw={'readonly': True}) specialist_id = IntegerField('Specialist', validators=[DataRequired()], render_kw={'readonly': True})
specialist_name = StringField('Specialist Name', validators=[DataRequired()], render_kw={'readonly': True}) specialist_name = StringField('Specialist Name', validators=[DataRequired()], render_kw={'readonly': True})
tenant_make_id = SelectField('Tenant Make', validators=[Optional()], coerce=int) tenant_make_id = SelectField('Tenant Make', validators=[Optional()], coerce=int)
tenant_make_name = StringField('Tenant Make Name', validators=[Optional()], render_kw={'readonly': True})
valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()]) valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()])
valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()]) valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()])

View File

@@ -705,6 +705,14 @@ def specialist_magic_link():
new_spec_ml_tenant.magic_link_code = new_specialist_magic_link.magic_link_code new_spec_ml_tenant.magic_link_code = new_specialist_magic_link.magic_link_code
new_spec_ml_tenant.tenant_id = tenant_id new_spec_ml_tenant.tenant_id = tenant_id
# Define the make valid for this magic link
make_id = SpecialistServices.get_specialist_system_field(new_specialist_magic_link.id,
"make", "tenant_make")
if make_id:
new_spec_ml_tenant.tenant_make_id = make_id
elif session.get('tenant').get('default_tenant_make_id'):
new_spec_ml_tenant.tenant_make_id = session.get('tenant').get('default_tenant_make_id')
db.session.add(new_specialist_magic_link) db.session.add(new_specialist_magic_link)
db.session.add(new_spec_ml_tenant) db.session.add(new_spec_ml_tenant)

View File

@@ -20,7 +20,41 @@ class TenantForm(FlaskForm):
website = StringField('Website', validators=[DataRequired(), Length(max=255)]) website = StringField('Website', validators=[DataRequired(), Length(max=255)])
# language fields # language fields
default_language = SelectField('Default Language', choices=[], validators=[DataRequired()]) default_language = SelectField('Default Language', choices=[], validators=[DataRequired()])
allowed_languages = SelectMultipleField('Allowed Languages', choices=[], validators=[DataRequired()]) # invoicing fields
currency = SelectField('Currency', choices=[], validators=[DataRequired()])
# Timezone
timezone = SelectField('Timezone', choices=[], validators=[DataRequired()])
# For Super Users only - Allow to assign the tenant to the partner
assign_to_partner = BooleanField('Assign to Partner', default=False)
# Embedding variables
submit = SubmitField('Submit')
def __init__(self, *args, **kwargs):
super(TenantForm, self).__init__(*args, **kwargs)
# initialise language fields
self.default_language.choices = [(lang, lang.lower()) for lang in current_app.config['SUPPORTED_LANGUAGES']]
# initialise currency field
self.currency.choices = [(curr, curr) for curr in current_app.config['SUPPORTED_CURRENCIES']]
# initialise timezone
self.timezone.choices = [(tz, tz) for tz in pytz.common_timezones]
# Initialize fallback algorithms
self.type.choices = [(t, t) for t in current_app.config['TENANT_TYPES']]
# Initialize default tenant make choices
tenant_id = session.get('tenant', {}).get('id') if 'tenant' in session else None
# Show field only for Super Users with partner in session
if not current_user.has_roles('Super User') or 'partner' not in session:
self._fields.pop('assign_to_partner', None)
class EditTenantForm(FlaskForm):
id = IntegerField('ID', widget=HiddenInput())
name = StringField('Name', validators=[DataRequired(), Length(max=80)])
code = StringField('Code', validators=[DataRequired()], render_kw={'readonly': True})
type = SelectField('Tenant Type', validators=[Optional()], default='Active')
website = StringField('Website', validators=[DataRequired(), Length(max=255)])
# language fields
default_language = SelectField('Default Language', choices=[], validators=[DataRequired()])
# invoicing fields # invoicing fields
currency = SelectField('Currency', choices=[], validators=[DataRequired()]) currency = SelectField('Currency', choices=[], validators=[DataRequired()])
# Timezone # Timezone
@@ -34,10 +68,9 @@ class TenantForm(FlaskForm):
submit = SubmitField('Submit') submit = SubmitField('Submit')
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(TenantForm, self).__init__(*args, **kwargs) super(EditTenantForm, self).__init__(*args, **kwargs)
# initialise language fields # initialise language fields
self.default_language.choices = [(lang, lang.lower()) for lang in current_app.config['SUPPORTED_LANGUAGES']] self.default_language.choices = [(lang, lang.lower()) for lang in current_app.config['SUPPORTED_LANGUAGES']]
self.allowed_languages.choices = [(lang, lang.lower()) for lang in current_app.config['SUPPORTED_LANGUAGES']]
# initialise currency field # initialise currency field
self.currency.choices = [(curr, curr) for curr in current_app.config['SUPPORTED_CURRENCIES']] self.currency.choices = [(curr, curr) for curr in current_app.config['SUPPORTED_CURRENCIES']]
# initialise timezone # initialise timezone
@@ -45,7 +78,7 @@ class TenantForm(FlaskForm):
# Initialize fallback algorithms # Initialize fallback algorithms
self.type.choices = [(t, t) for t in current_app.config['TENANT_TYPES']] self.type.choices = [(t, t) for t in current_app.config['TENANT_TYPES']]
# Initialize default tenant make choices # Initialize default tenant make choices
tenant_id = session.get('tenant', {}).get('id') if 'tenant' in session else None tenant_id = self.id.data
if tenant_id: if tenant_id:
tenant_makes = TenantMake.query.filter_by(tenant_id=tenant_id, active=True).all() tenant_makes = TenantMake.query.filter_by(tenant_id=tenant_id, active=True).all()
self.default_tenant_make_id.choices = [(str(make.id), make.name) for make in tenant_makes] self.default_tenant_make_id.choices = [(str(make.id), make.name) for make in tenant_makes]
@@ -154,6 +187,13 @@ def validate_make_name(form, field):
class TenantMakeForm(DynamicFormBase): class TenantMakeForm(DynamicFormBase):
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name])
description = TextAreaField('Description', validators=[Optional()])
active = BooleanField('Active', validators=[Optional()], default=True)
website = StringField('Website', validators=[DataRequired(), Length(max=255)])
logo_url = StringField('Logo URL', validators=[Optional(), Length(max=255)])
class EditTenantMakeForm(DynamicFormBase):
id = IntegerField('ID', widget=HiddenInput()) id = IntegerField('ID', widget=HiddenInput())
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name]) name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name])
description = TextAreaField('Description', validators=[Optional()]) description = TextAreaField('Description', validators=[Optional()])

View File

@@ -12,7 +12,7 @@ from common.utils.dynamic_field_utils import create_default_config_from_type_con
from common.utils.security_utils import send_confirmation_email, send_reset_email from common.utils.security_utils import send_confirmation_email, send_reset_email
from config.type_defs.service_types import SERVICE_TYPES from config.type_defs.service_types import SERVICE_TYPES
from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm, TenantSelectionForm, \ from .user_forms import TenantForm, CreateUserForm, EditUserForm, TenantDomainForm, TenantSelectionForm, \
TenantProjectForm, EditTenantProjectForm, TenantMakeForm TenantProjectForm, EditTenantProjectForm, TenantMakeForm, EditTenantForm
from common.utils.database import Database from common.utils.database import Database
from common.utils.view_assistants import prepare_table_for_macro, form_validation_failed from common.utils.view_assistants import prepare_table_for_macro, form_validation_failed
from common.utils.simple_encryption import generate_api_key from common.utils.simple_encryption import generate_api_key
@@ -53,10 +53,6 @@ def tenant():
new_tenant = Tenant() new_tenant = Tenant()
form.populate_obj(new_tenant) form.populate_obj(new_tenant)
# Convert default_tenant_make_id to integer if not empty
if form.default_tenant_make_id.data:
new_tenant.default_tenant_make_id = int(form.default_tenant_make_id.data)
timestamp = dt.now(tz.utc) timestamp = dt.now(tz.utc)
new_tenant.created_at = timestamp new_tenant.created_at = timestamp
new_tenant.updated_at = timestamp new_tenant.updated_at = timestamp
@@ -116,7 +112,7 @@ def tenant():
@roles_accepted('Super User', 'Partner Admin') @roles_accepted('Super User', 'Partner Admin')
def edit_tenant(tenant_id): def edit_tenant(tenant_id):
tenant = Tenant.query.get_or_404(tenant_id) # This will return a 404 if no tenant is found tenant = Tenant.query.get_or_404(tenant_id) # This will return a 404 if no tenant is found
form = TenantForm(obj=tenant) form = EditTenantForm(obj=tenant)
if form.validate_on_submit(): if form.validate_on_submit():
# Populate the tenant with form data # Populate the tenant with form data
@@ -471,7 +467,7 @@ def edit_tenant_domain(tenant_domain_id):
def tenant_overview(): def tenant_overview():
tenant_id = session['tenant']['id'] tenant_id = session['tenant']['id']
tenant = Tenant.query.get_or_404(tenant_id) tenant = Tenant.query.get_or_404(tenant_id)
form = TenantForm(obj=tenant) form = EditTenantForm(obj=tenant)
# Zet de waarde van default_tenant_make_id # Zet de waarde van default_tenant_make_id
if tenant.default_tenant_make_id: if tenant.default_tenant_make_id:
@@ -683,7 +679,8 @@ def tenant_makes():
page = request.args.get('page', 1, type=int) page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int) per_page = request.args.get('per_page', 10, type=int)
query = TenantMake.query.order_by(TenantMake.id) tenant_id = session['tenant']['id']
query = TenantMake.query.filter_by(tenant_id=tenant_id).order_by(TenantMake.id)
pagination = query.paginate(page=page, per_page=per_page) pagination = query.paginate(page=page, per_page=per_page)
tenant_makes = pagination.items tenant_makes = pagination.items

View File

@@ -1,6 +1,7 @@
import logging import logging
import os import os
from flask import Flask, jsonify
from flask import Flask, jsonify, request
from werkzeug.middleware.proxy_fix import ProxyFix from werkzeug.middleware.proxy_fix import ProxyFix
import logging.config import logging.config
@@ -74,6 +75,13 @@ def create_app(config_file=None):
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("-------------------------------------------------------------------------------------------------")
# @app.before_request
# def app_before_request():
# app.logger.debug(f'App before request: {request.path} ===== Method: {request.method} =====')
# app.logger.debug(f'Full URL: {request.url}')
# app.logger.debug(f'Endpoint: {request.endpoint}')
return app return app

View File

@@ -11,11 +11,11 @@
<!-- Custom theme colors from tenant settings --> <!-- Custom theme colors from tenant settings -->
<style> <style>
:root { :root {
--primary-color: {{ customization.primary_color|default('#007bff') }}; --primary-color: {{ customisation.primary_color|default('#007bff') }};
--secondary-color: {{ customization.secondary_color|default('#6c757d') }}; --secondary-color: {{ customisation.secondary_color|default('#6c757d') }};
--background-color: {{ customization.background_color|default('#ffffff') }}; --background-color: {{ customisation.background_color|default('#ffffff') }};
--text-color: {{ customization.text_color|default('#212529') }}; --text-color: {{ customisation.text_color|default('#212529') }};
--sidebar-color: {{ customization.sidebar_color|default('#f8f9fa') }}; --sidebar-color: {{ customisation.sidebar_color|default('#f8f9fa') }};
} }
</style> </style>

View File

@@ -3,13 +3,32 @@ from flask import Blueprint, render_template, request, session, current_app, jso
from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.exc import SQLAlchemyError
from common.extensions import db from common.extensions import db
from common.models.user import Tenant, SpecialistMagicLinkTenant from common.models.user import Tenant, SpecialistMagicLinkTenant, TenantMake
from common.models.interaction import SpecialistMagicLink, Specialist, ChatSession, Interaction from common.models.interaction import SpecialistMagicLink, Specialist, ChatSession, Interaction
from common.services.interaction.specialist_services import SpecialistServices from common.services.interaction.specialist_services import SpecialistServices
from common.utils.database import Database from common.utils.database import Database
from common.utils.chat_utils import get_default_chat_customisation from common.utils.chat_utils import get_default_chat_customisation
chat_bp = Blueprint('chat', __name__) chat_bp = Blueprint('chat_bp', __name__, url_prefix='/chat')
@chat_bp.before_request
def log_before_request():
current_app.logger.debug(f'Before request: {request.path} =====================================')
@chat_bp.after_request
def log_after_request(response):
return response
# @chat_bp.before_request
# def before_request():
# try:
# mw_before_request()
# except Exception as e:
# current_app.logger.error(f'Error switching schema in Document Blueprint: {e}')
# raise
@chat_bp.route('/') @chat_bp.route('/')
def index(): def index():
@@ -31,14 +50,12 @@ def chat(magic_link_code):
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.html', message="Invalid magic link code.")
tenant_id = magic_link_tenant.tenant_id
# Get tenant information # Get tenant information
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.html', message="Tenant not found.")
# Switch to tenant schema # Switch to tenant schema
Database(tenant_id).switch_schema() Database(tenant_id).switch_schema()
@@ -48,6 +65,12 @@ def chat(magic_link_code):
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.html', message="Specialist configuration not found.")
# Get relevant TenantMake
tenant_make = TenantMake.query.get(specialist_ml.tenant_make_id)
if not tenant_make:
current_app.logger.error(f"Tenant make not found: {specialist_ml.tenant_make_id}")
return render_template('error.html', message="Tenant make not found.")
# 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:
@@ -55,13 +78,13 @@ def chat(magic_link_code):
return render_template('error.html', message="Specialist not found.") return render_template('error.html', message="Specialist not found.")
# Store necessary information in session # Store necessary information in session
session['tenant_id'] = tenant_id session['tenant'] = tenant.to_dict()
session['specialist_id'] = specialist_ml.specialist_id session['specialist'] = specialist.to_dict()
session['specialist_args'] = specialist_ml.specialist_args or {} session['magic_link'] = specialist_ml.to_dict()
session['magic_link_code'] = magic_link_code session['tenant_make'] = tenant_make.to_dict()
# Get customisation options with defaults # Get customisation options with defaults
customisation = get_default_chat_customisation(tenant.chat_customisation_options) customisation = get_default_chat_customisation(tenant_make.chat_customisation_options)
# Start a new chat session # Start a new chat session
session['chat_session_id'] = SpecialistServices.start_session() session['chat_session_id'] = SpecialistServices.start_session()