- Move global config files to globals iso global folder, as the name global conflicts with python language

- Creation of Traicie Vancancy Definition specialist
- Allow to invoke non-interaction specialists from withing Evie's mgmt interface (eveai_app)
- Improvements to crewai specialized classes
- Introduction to json editor for showing specialists arguments and results in a better way
- Introduction of more complex pagination (adding extra arguments) by adding a global 'get_pagination_html'
- Allow follow-up of ChatSession / Specialist execution
- Improvement in logging of Specialists (but needs to be finished)
This commit is contained in:
Josako
2025-05-26 11:26:03 +02:00
parent d789e431ca
commit 1fdbd2ff45
94 changed files with 1657 additions and 443 deletions

View File

@@ -1,5 +1,7 @@
import ast
import json
from datetime import datetime as dt, timezone as tz
import time
from flask import request, redirect, flash, render_template, Blueprint, session, current_app, jsonify, url_for
from flask_security import roles_accepted
@@ -14,7 +16,9 @@ from common.models.interaction import (ChatSession, Interaction, InteractionEmbe
EveAIAgent, EveAITask, EveAITool, EveAIAssetVersion)
from common.extensions import db, cache_manager
from common.services.interaction.specialist_services import SpecialistServices
from common.utils.asset_utils import create_asset_stack, add_asset_version_file
from common.utils.execution_progress import ExecutionProgressTracker
from common.utils.model_logging_utils import set_logging_information, update_logging_information
from common.utils.middleware import mw_before_request
@@ -25,7 +29,7 @@ from common.utils.specialist_utils import initialize_specialist
from config.type_defs.specialist_types import SPECIALIST_TYPES
from .interaction_forms import (SpecialistForm, EditSpecialistForm, EditEveAIAgentForm, EditEveAITaskForm,
EditEveAIToolForm, AddEveAIAssetForm, EditEveAIAssetVersionForm)
EditEveAIToolForm, AddEveAIAssetForm, EditEveAIAssetVersionForm, ExecuteSpecialistForm)
interaction_bp = Blueprint('interaction_bp', __name__, url_prefix='/interaction')
@@ -72,10 +76,13 @@ def handle_chat_session_selection():
cs_id = ast.literal_eval(chat_session_identification).get('value')
action = request.form['action']
current_app.logger.debug(f'Handle Chat Session Selection Action: {action}')
match action:
case 'view_chat_session':
return redirect(prefixed_url_for('interaction_bp.view_chat_session', chat_session_id=cs_id))
case 'chat_session_interactions':
return redirect(prefixed_url_for('interaction_bp.session_interactions', chat_session_id=cs_id))
# Add more conditions for other actions
return redirect(prefixed_url_for('interaction_bp.chat_sessions'))
@@ -124,8 +131,14 @@ def view_chat_session(chat_session_id):
@interaction_bp.route('/view_chat_session_by_session_id/<session_id>', methods=['GET'])
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
def view_chat_session_by_session_id(session_id):
"""
Deze route accepteert een session_id (string) en stuurt door naar view_chat_session met het juiste chat_session_id (integer)
"""
# Vind de chat session op basis van session_id
chat_session = ChatSession.query.filter_by(session_id=session_id).first_or_404()
show_chat_session(chat_session)
# Nu we het chat_session object hebben, kunnen we de bestaande functie hergebruiken
return view_chat_session(chat_session.id)
def show_chat_session(chat_session):
@@ -303,6 +316,8 @@ def handle_specialist_selection():
if action == "edit_specialist":
return redirect(prefixed_url_for('interaction_bp.edit_specialist', specialist_id=specialist_id))
elif action == "execute_specialist":
return redirect(prefixed_url_for('interaction_bp.execute_specialist', specialist_id=specialist_id))
return redirect(prefixed_url_for('interaction_bp.specialists'))
@@ -391,9 +406,8 @@ def edit_tool(tool_id):
form = EditEveAIToolForm(obj=tool)
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
return render_template('interaction/components/edit_tool.html',
form=form,
tool=tool)
return render_template('interaction/components/edit_tool.html', form=form, tool=tool)
return None
@interaction_bp.route('/tool/<int:tool_id>/save', methods=['POST'])
@@ -546,3 +560,106 @@ def edit_asset_version(asset_version_id):
return render_template('interaction/edit_asset_version.html', form=form)
@interaction_bp.route('/execute_specialist/<int:specialist_id>', methods=['GET', 'POST'])
def execute_specialist(specialist_id):
specialist = Specialist.query.get_or_404(specialist_id)
specialist_config = cache_manager.specialists_config_cache.get_config(specialist.type, specialist.type_version)
if specialist_config.get('chat', True):
flash("Only specialists that don't require interactions can be executed this way!", 'error')
return redirect(prefixed_url_for('interaction_bp.specialists'))
form = ExecuteSpecialistForm(request.form, obj=specialist)
arguments_config = specialist_config.get('arguments', None)
if arguments_config:
form.add_dynamic_fields('arguments', arguments_config)
if form.validate_on_submit():
# We're only interested in gathering the dynamic arguments
arguments = form.get_dynamic_data("arguments")
current_app.logger.debug(f"Executing specialist {specialist.id} with arguments: {arguments}")
session_id = SpecialistServices.start_session()
execution_task = SpecialistServices.execute_specialist(
tenant_id=session.get('tenant').get('id'),
specialist_id=specialist_id,
specialist_arguments=arguments,
session_id=session_id,
user_timezone=session.get('tenant').get('timezone')
)
current_app.logger.debug(f"Execution task for specialist {specialist.id} created: {execution_task}")
return redirect(prefixed_url_for('interaction_bp.session_interactions_by_session_id', session_id=session_id))
return render_template('interaction/execute_specialist.html', form=form)
@interaction_bp.route('/session_interactions_by_session_id/<session_id>', methods=['GET'])
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
def session_interactions_by_session_id(session_id):
"""
This route shows all interactions for a given session_id (string).
If the chat_session doesn't exist yet, it will wait for up to 10 seconds
(with 1 second intervals) until it becomes available.
"""
waiting_message = request.args.get('waiting', 'false') == 'true'
# Try up to 10 times with 1 second pause
max_tries = 10
current_try = 1
while current_try <= max_tries:
chat_session = ChatSession.query.filter_by(session_id=session_id).first()
if chat_session:
# Session found, display the interactions
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 10, type=int)
query = Interaction.query.filter_by(chat_session_id=chat_session.id).order_by(Interaction.question_at)
pagination = query.paginate(page=page, per_page=per_page, error_out=False)
interactions = pagination.items
rows = prepare_table_for_macro(interactions, [('id', ''), ('question_at', ''), ('detailed_question_at', ''),
('answer_at', ''), ('processing_error', '')])
# Define a callback to make a URL for a given page and the same session_id
def make_page_url(page_num):
return prefixed_url_for('interaction_bp.session_interactions_by_session_id', session_id=session_id,
page=page_num)
return render_template('interaction/session_interactions.html',
chat_session=chat_session, rows=rows, pagination=pagination,
make_page_url=make_page_url)
# Session not found, wait and try again
if current_try < max_tries:
current_try += 1
time.sleep(1)
else:
# Maximum number of attempts reached
break
# If we're here, the session wasn't found after the maximum number of attempts
flash(f'The chat session with ID {session_id} could not be found after {max_tries} attempts. '
f'The session may still be in the process of being created or the ID might be incorrect.', 'warning')
# Show a waiting page with auto-refresh if we haven't shown a waiting message yet
if not waiting_message:
return render_template('interaction/waiting_for_session.html',
session_id=session_id,
refresh_url=prefixed_url_for('interaction_bp.session_interactions_by_session_id',
session_id=session_id,
waiting='true'))
# If we've already shown a waiting message and still don't have a session, go back to the specialists page
return redirect(prefixed_url_for('interaction_bp.specialists'))
@interaction_bp.route('/session_interactions/<chat_session_id>', methods=['GET'])
@roles_accepted('Super User', 'Partner Admin', 'Tenant Admin')
def session_interactions(chat_session_id):
"""
This route shows all interactions for a given chat_session_id (int).
"""
chat_session = ChatSession.query.get_or_404(chat_session_id)
return session_interactions_by_session_id(chat_session.session_id)