Compare commits
3 Commits
develop
...
feature/Ad
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
443104dfc7 | ||
|
|
f2604db5a9 | ||
|
|
78043ab3ef |
@@ -24,6 +24,7 @@ class Database:
|
||||
"""
|
||||
schema = session.info.get("tenant_schema")
|
||||
if schema:
|
||||
current_app.logger.debug(f"DBCTX tx_begin schema={schema}")
|
||||
try:
|
||||
connection.exec_driver_sql(f'SET LOCAL search_path TO "{schema}", public')
|
||||
# Optional visibility/logging for debugging
|
||||
|
||||
@@ -35,7 +35,8 @@ def is_valid_tenant(tenant_id):
|
||||
if tenant_id == 1: # The 'root' tenant, is always valid
|
||||
return True
|
||||
tenant = Tenant.query.get(tenant_id)
|
||||
Database(tenant).switch_schema()
|
||||
# Use the tenant_id (schema name), not the Tenant object, to switch schema
|
||||
Database(tenant_id).switch_schema()
|
||||
if tenant is None:
|
||||
raise EveAITenantNotFound()
|
||||
elif tenant.type == 'Inactive':
|
||||
|
||||
15
config/prompts/globals/user_action_classifier/1.0.0.yaml
Normal file
15
config/prompts/globals/user_action_classifier/1.0.0.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
version: "1.0.0"
|
||||
content: |
|
||||
Classify the prompt you receive from an end user, according to the following information:
|
||||
|
||||
{user_action_classes}
|
||||
|
||||
Use the CLASS DESCRIPTION to identify the CLASS of the question asked. Return the value of CLASS. If the prompt doesn't correspond to any CLASS DESCRIPTION, return NONE. No layout is required.
|
||||
|
||||
llm_model: "mistral.mistral-small-latest"
|
||||
temperature: 0.7
|
||||
metadata:
|
||||
author: "Josako"
|
||||
date_added: "2025-11-14"
|
||||
description: "Assistant to classify user intent"
|
||||
changes: "Initial version"
|
||||
@@ -9,6 +9,12 @@
|
||||
{% block content %}
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
{{ form.hidden_tag() }}
|
||||
{# Debug: render CSRF veld expliciet om aanwezigheid in de DOM te garanderen #}
|
||||
{% if form.csrf_token %}{{ form.csrf_token }}{% endif %}
|
||||
<script>
|
||||
// Client-side debug: bevestig dat het CSRF veld in de DOM staat
|
||||
console.debug('[add_document] CSRF present in DOM?', !!document.querySelector('input[name="csrf_token"]'));
|
||||
</script>
|
||||
{% set disabled_fields = [] %}
|
||||
{% set exclude_fields = [] %}
|
||||
{% for field in form.get_static_fields() %}
|
||||
|
||||
@@ -11,18 +11,18 @@
|
||||
{{ form.hidden_tag() }}
|
||||
{% set disabled_fields = [] %}
|
||||
{% set exclude_fields = [] %}
|
||||
{% for field in form.get_static_fields() %}
|
||||
{% for field in form %}
|
||||
{{ render_field(field, disabled_fields, exclude_fields) }}
|
||||
{% endfor %}
|
||||
<!-- Render Dynamic Fields -->
|
||||
{% for collection_name, fields in form.get_dynamic_fields().items() %}
|
||||
{% if fields|length > 0 %}
|
||||
<h4 class="mt-4">{{ collection_name }}</h4>
|
||||
{% endif %}
|
||||
{% for field in fields %}
|
||||
{{ render_field(field, disabled_fields, exclude_fields) }}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{# {% for collection_name, fields in form.get_dynamic_fields().items() %}#}
|
||||
{# {% if fields|length > 0 %}#}
|
||||
{# <h4 class="mt-4">{{ collection_name }}</h4>#}
|
||||
{# {% endif %}#}
|
||||
{# {% for field in fields %}#}
|
||||
{# {{ render_field(field, disabled_fields, exclude_fields) }}#}
|
||||
{# {% endfor %}#}
|
||||
{# {% endfor %}#}
|
||||
<button type="submit" class="btn btn-primary">Register Tenant Make</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
|
||||
@@ -6,6 +6,7 @@ from flask_security import roles_accepted, current_user
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy.orm import aliased
|
||||
from werkzeug.utils import secure_filename
|
||||
from werkzeug.datastructures import CombinedMultiDict
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
import requests
|
||||
from requests.exceptions import SSLError, HTTPError
|
||||
@@ -354,7 +355,17 @@ def handle_retriever_selection():
|
||||
@document_bp.route('/add_document', methods=['GET', 'POST'])
|
||||
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
|
||||
def add_document():
|
||||
form = AddDocumentForm(request.form)
|
||||
# Log vroege request-info om uploadproblemen te diagnosticeren
|
||||
try:
|
||||
current_app.logger.debug(
|
||||
f"[add_document] method={request.method}, content_type={request.content_type}, "
|
||||
f"files_keys={list(request.files.keys())}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Bind expliciet zowel form- als file-data aan de form (belangrijk voor FileField & CSRF)
|
||||
form = AddDocumentForm(CombinedMultiDict([request.form, request.files]))
|
||||
catalog_id = session.get('catalog_id', None)
|
||||
if catalog_id is None:
|
||||
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
|
||||
@@ -364,6 +375,38 @@ def add_document():
|
||||
if catalog.configuration and len(catalog.configuration) > 0:
|
||||
form.add_dynamic_fields("tagging_fields", catalog.configuration)
|
||||
|
||||
current_app.logger.debug("In Add Document")
|
||||
|
||||
# Extra debug logging om CSRF/payload te controleren
|
||||
try:
|
||||
current_app.logger.debug(
|
||||
f"[add_document] request.form keys: {list(request.form.keys())}"
|
||||
)
|
||||
current_app.logger.debug(
|
||||
f"[add_document] csrf_token in form? {request.form.get('csrf_token') is not None}"
|
||||
)
|
||||
try:
|
||||
has_csrf_field = hasattr(form, 'csrf_token')
|
||||
current_app.logger.debug(
|
||||
f"[add_document] form has csrf field? {has_csrf_field}"
|
||||
)
|
||||
if has_csrf_field:
|
||||
# Let op: we loggen geen tokenwaarde om lekken te vermijden; enkel aanwezigheid
|
||||
current_app.logger.debug(
|
||||
"[add_document] form.csrf_token field is present on form object"
|
||||
)
|
||||
# Bevestig of de CSRF-waarde effectief in de form is gebonden
|
||||
try:
|
||||
current_app.logger.debug(
|
||||
f"[add_document] csrf bound? data_present={bool(form.csrf_token.data)} field_name={getattr(form.csrf_token, 'name', None)}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
current_app.logger.info(f'Adding Document for {catalog_id}')
|
||||
@@ -400,6 +443,25 @@ def add_document():
|
||||
except Exception as e:
|
||||
current_app.logger.error(f'Error adding document: {str(e)}')
|
||||
flash('An error occurred while adding the document.', 'danger')
|
||||
else:
|
||||
# Toon en log validatiefouten als de submit faalt
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
current_app.logger.warning(
|
||||
f"[add_document] form validation failed. errors={getattr(form, 'errors', {})}"
|
||||
)
|
||||
current_app.logger.debug(
|
||||
f"[add_document] request.files keys after validation: {list(request.files.keys())}"
|
||||
)
|
||||
current_app.logger.debug(
|
||||
f"[add_document] request.form keys after validation: {list(request.form.keys())}"
|
||||
)
|
||||
current_app.logger.debug(
|
||||
f"[add_document] csrf_token in form after validation? {request.form.get('csrf_token') is not None}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
form_validation_failed(request, form)
|
||||
|
||||
return render_template('document/add_document.html', form=form)
|
||||
|
||||
@@ -407,7 +469,16 @@ def add_document():
|
||||
@document_bp.route('/add_url', methods=['GET', 'POST'])
|
||||
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
|
||||
def add_url():
|
||||
form = AddURLForm(request.form)
|
||||
# Log vroege request-info om submitproblemen te diagnosticeren
|
||||
try:
|
||||
current_app.logger.debug(
|
||||
f"[add_url] method={request.method}, content_type={request.content_type}, files_keys={list(request.files.keys())}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Bind expliciet zowel form- als file-data (consistentie en duidelijkheid)
|
||||
form = AddURLForm(CombinedMultiDict([request.form, request.files]))
|
||||
catalog_id = session.get('catalog_id', None)
|
||||
if catalog_id is None:
|
||||
flash('You need to set a Session Catalog before adding Documents or URLs', 'warning')
|
||||
@@ -417,6 +488,15 @@ def add_url():
|
||||
if catalog.configuration and len(catalog.configuration) > 0:
|
||||
form.add_dynamic_fields("tagging_fields", catalog.configuration)
|
||||
url=""
|
||||
# Kleine debug om te zien of CSRF aan de form gebonden is
|
||||
try:
|
||||
if hasattr(form, 'csrf_token'):
|
||||
current_app.logger.debug(
|
||||
f"[add_url] csrf bound? data_present={bool(form.csrf_token.data)} field_name={getattr(form.csrf_token, 'name', None)}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
tenant_id = session['tenant']['id']
|
||||
@@ -462,6 +542,15 @@ def add_url():
|
||||
except Exception as e:
|
||||
current_app.logger.error(f'Error adding document: {str(e)}')
|
||||
flash('An error occurred while adding the document.', 'danger')
|
||||
else:
|
||||
if request.method == 'POST':
|
||||
try:
|
||||
current_app.logger.warning(
|
||||
f"[add_url] form validation failed. errors={getattr(form, 'errors', {})}"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
form_validation_failed(request, form)
|
||||
|
||||
return render_template('document/add_url.html', form=form)
|
||||
|
||||
|
||||
@@ -88,13 +88,13 @@ class BaseUserForm(FlaskForm):
|
||||
last_name = StringField('Last Name', validators=[DataRequired(), Length(max=80)])
|
||||
valid_to = DateField('Valid to', id='form-control datepicker', validators=[Optional()])
|
||||
tenant_id = IntegerField('Tenant ID', validators=[NumberRange(min=0)])
|
||||
roles = SelectMultipleField('Roles', coerce=int)
|
||||
selected_role_ids = SelectMultipleField('Roles', coerce=int)
|
||||
is_primary_contact = BooleanField('Primary Contact')
|
||||
is_financial_contact = BooleanField('Financial Contact')
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(BaseUserForm, self).__init__(*args, **kwargs)
|
||||
self.roles.choices = UserServices.get_assignable_roles()
|
||||
self.selected_role_ids.choices = UserServices.get_assignable_roles()
|
||||
|
||||
|
||||
class CreateUserForm(BaseUserForm):
|
||||
@@ -177,7 +177,7 @@ def validate_make_name(form, field):
|
||||
raise ValidationError(f'A Make with name "{field.data}" already exists. Choose another name.')
|
||||
|
||||
|
||||
class TenantMakeForm(DynamicFormBase):
|
||||
class TenantMakeForm(FlaskForm):
|
||||
name = StringField('Name', validators=[DataRequired(), Length(max=50), validate_make_name])
|
||||
description = TextAreaField('Description', validators=[Optional()])
|
||||
active = BooleanField('Active', validators=[Optional()], default=True)
|
||||
|
||||
@@ -217,21 +217,15 @@ def user():
|
||||
if form.validate_on_submit():
|
||||
current_app.logger.info(f"Adding User for tenant {session['tenant']['id']} ")
|
||||
|
||||
new_user = User(user_name=form.user_name.data,
|
||||
email=form.email.data,
|
||||
first_name=form.first_name.data,
|
||||
last_name=form.last_name.data,
|
||||
valid_to=form.valid_to.data,
|
||||
tenant_id=form.tenant_id.data,
|
||||
fs_uniquifier=uuid.uuid4().hex,
|
||||
)
|
||||
new_user = User()
|
||||
form.populate_obj(new_user)
|
||||
|
||||
timestamp = dt.now(tz.utc)
|
||||
new_user.created_at = timestamp
|
||||
new_user.updated_at = timestamp
|
||||
|
||||
# Add roles
|
||||
for role_id in form.roles.data:
|
||||
for role_id in form.selected_role_ids.data:
|
||||
the_role = Role.query.get(role_id)
|
||||
new_user.roles.append(the_role)
|
||||
|
||||
@@ -266,18 +260,18 @@ def user():
|
||||
@roles_accepted('Super User', 'Tenant Admin', 'Partner Admin')
|
||||
def edit_user(user_id):
|
||||
user = User.query.get_or_404(user_id) # This will return a 404 if no user is found
|
||||
tenant_id = session.get('tenant').get('id')
|
||||
form = EditUserForm(obj=user)
|
||||
|
||||
if form.validate_on_submit():
|
||||
# Populate the user with form data
|
||||
user.first_name = form.first_name.data
|
||||
user.last_name = form.last_name.data
|
||||
user.valid_to = form.valid_to.data
|
||||
user.updated_at = dt.now(tz.utc)
|
||||
form.populate_obj(user)
|
||||
timestamp = dt.now(tz.utc)
|
||||
user.updated_at = timestamp
|
||||
|
||||
# Update roles
|
||||
current_roles = set(role.id for role in user.roles)
|
||||
selected_roles = set(form.roles.data)
|
||||
selected_roles = set(form.selected_role_ids.data)
|
||||
if UserServices.validate_role_assignments(selected_roles):
|
||||
# Add new roles
|
||||
for role_id in selected_roles - current_roles:
|
||||
@@ -303,7 +297,7 @@ def edit_user(user_id):
|
||||
else:
|
||||
form_validation_failed(request, form)
|
||||
|
||||
form.roles.data = [role.id for role in user.roles]
|
||||
form.selected_role_ids.data = [role.id for role in user.roles]
|
||||
return render_template('user/edit_user.html', form=form, user_id=user_id)
|
||||
|
||||
|
||||
@@ -577,10 +571,12 @@ def delete_tenant_project(tenant_project_id):
|
||||
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
|
||||
def tenant_make():
|
||||
form = TenantMakeForm()
|
||||
customisation_config = cache_manager.customisations_config_cache.get_config("CHAT_CLIENT_CUSTOMISATION")
|
||||
default_customisation_options = create_default_config_from_type_config(customisation_config["configuration"])
|
||||
current_app.logger.debug(f"ìn tenant_make view")
|
||||
# customisation_config = cache_manager.customisations_config_cache.get_config("CHAT_CLIENT_CUSTOMISATION")
|
||||
# default_customisation_options = create_default_config_from_type_config(customisation_config["configuration"])
|
||||
|
||||
if form.validate_on_submit():
|
||||
current_app.logger.debug(f"in tenant_make form validate")
|
||||
tenant_id = session['tenant']['id']
|
||||
new_tenant_make = TenantMake()
|
||||
form.populate_obj(new_tenant_make)
|
||||
@@ -602,6 +598,8 @@ def tenant_make():
|
||||
flash(f'Failed to add Tenant Make. Error: {e}', 'danger')
|
||||
current_app.logger.error(f'Failed to add Tenant Make {new_tenant_make.name}'
|
||||
f'for tenant {tenant_id}. Error: {str(e)}')
|
||||
else:
|
||||
flash('Please fill in all required fields.', 'information')
|
||||
|
||||
return render_template('user/tenant_make.html', form=form)
|
||||
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
INSUFFICIENT_INFORMATION_MESSAGES = [
|
||||
"I'm afraid I don't have enough information to answer that properly. Feel free to ask something else!",
|
||||
"There isn’t enough data available right now to give you a clear answer. You're welcome to rephrase or ask a different question.",
|
||||
"Sorry, I can't provide a complete answer based on the current information. Would you like to try asking something else?",
|
||||
"I don’t have enough details to give you a confident answer. You can always ask another question if you’d like.",
|
||||
"Unfortunately, I can’t answer that accurately with the information at hand. Please feel free to ask something else.",
|
||||
"That’s a great question, but I currently lack the necessary information to respond properly. Want to ask something different?",
|
||||
"I wish I could help more, but the data I have isn't sufficient to answer this. You’re welcome to explore other questions.",
|
||||
"There’s not enough context for me to provide a good answer. Don’t hesitate to ask another question if you'd like!",
|
||||
"I'm not able to give a definitive answer to that. Perhaps try a different question or angle?",
|
||||
"Thanks for your question. At the moment, I can’t give a solid answer — but I'm here if you want to ask something else!"
|
||||
]
|
||||
@@ -19,19 +19,7 @@ from eveai_chat_workers.specialists.crewai_base_specialist import CrewAIBaseSpec
|
||||
from eveai_chat_workers.specialists.specialist_typing import SpecialistResult, SpecialistArguments
|
||||
from eveai_chat_workers.outputs.globals.rag.rag_v1_0 import RAGOutput
|
||||
from eveai_chat_workers.specialists.crewai_base_classes import EveAICrewAICrew, EveAICrewAIFlow, EveAIFlowState
|
||||
|
||||
INSUFFICIENT_INFORMATION_MESSAGES = [
|
||||
"I'm afraid I don't have enough information to answer that properly. Feel free to ask something else!",
|
||||
"There isn’t enough data available right now to give you a clear answer. You're welcome to rephrase or ask a different question.",
|
||||
"Sorry, I can't provide a complete answer based on the current information. Would you like to try asking something else?",
|
||||
"I don’t have enough details to give you a confident answer. You can always ask another question if you’d like.",
|
||||
"Unfortunately, I can’t answer that accurately with the information at hand. Please feel free to ask something else.",
|
||||
"That’s a great question, but I currently lack the necessary information to respond properly. Want to ask something different?",
|
||||
"I wish I could help more, but the data I have isn't sufficient to answer this. You’re welcome to explore other questions.",
|
||||
"There’s not enough context for me to provide a good answer. Don’t hesitate to ask another question if you'd like!",
|
||||
"I'm not able to give a definitive answer to that. Perhaps try a different question or angle?",
|
||||
"Thanks for your question. At the moment, I can’t give a solid answer — but I'm here if you want to ask something else!"
|
||||
]
|
||||
from eveai_chat_workers.definitions.messages.globals.rag_messages import INSUFFICIENT_INFORMATION_MESSAGES
|
||||
|
||||
class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
"""
|
||||
|
||||
@@ -87,7 +87,6 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def execute_initial_state(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult:
|
||||
self.log_tuning("Traicie KO Criteria Interview Definition Specialist initial_state_execution started", {})
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from eveai_chat_workers.outputs.traicie.knockout_questions.knockout_questions_v1
|
||||
from eveai_chat_workers.specialists.crewai_base_classes import EveAICrewAICrew, EveAICrewAIFlow, EveAIFlowState
|
||||
from eveai_chat_workers.specialists.crewai_base_specialist import CrewAIBaseSpecialistExecutor
|
||||
from eveai_chat_workers.specialists.specialist_typing import SpecialistResult, SpecialistArguments
|
||||
from eveai_chat_workers.definitions.messages.globals.rag_messages import INSUFFICIENT_INFORMATION_MESSAGES
|
||||
|
||||
INITIALISATION_MESSAGES = [
|
||||
"Great! Let's see if this job might be a match for you by going through a few questions.",
|
||||
@@ -85,18 +86,6 @@ TRY_TO_START_SELECTION_QUESTIONS = [
|
||||
"Understood! However, we can't proceed without initiating the process. Would you like to start it now after all?",
|
||||
"We appreciate your honesty. Just to clarify: the process only continues if we begin the selection. Shall we go ahead?"
|
||||
]
|
||||
INSUFFICIENT_INFORMATION_MESSAGES = [
|
||||
"I'm afraid I don't have enough information to answer that properly. Feel free to ask something else!",
|
||||
"There isn’t enough data available right now to give you a clear answer. You're welcome to rephrase or ask a different question.",
|
||||
"Sorry, I can't provide a complete answer based on the current information. Would you like to try asking something else?",
|
||||
"I don’t have enough details to give you a confident answer. You can always ask another question if you’d like.",
|
||||
"Unfortunately, I can’t answer that accurately with the information at hand. Please feel free to ask something else.",
|
||||
"That’s a great question, but I currently lack the necessary information to respond properly. Want to ask something different?",
|
||||
"I wish I could help more, but the data I have isn't sufficient to answer this. You’re welcome to explore other questions.",
|
||||
"There’s not enough context for me to provide a good answer. Don’t hesitate to ask another question if you'd like!",
|
||||
"I'm not able to give a definitive answer to that. Perhaps try a different question or angle?",
|
||||
"Thanks for your question. At the moment, I can’t give a solid answer — but I'm here if you want to ask something else!"
|
||||
]
|
||||
KO_CRITERIA_NOT_MET_MESSAGES = [
|
||||
"Thank you for your answers. Based on your responses, we won't be moving forward with this particular role. We do encourage you to keep an eye on our website for future opportunities.",
|
||||
"We appreciate the time you took to answer our questions. At this point, we won’t be proceeding with your application, but feel free to check our website regularly for new vacancies.",
|
||||
|
||||
Reference in New Issue
Block a user