- Started addition of Assets (to e.g. handle document templates).
- To be continued (Models, first views are ready)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import IntegerField, FloatField, BooleanField, StringField, TextAreaField, validators, ValidationError
|
||||
from wtforms import (IntegerField, FloatField, BooleanField, StringField, TextAreaField, FileField,
|
||||
validators, ValidationError)
|
||||
from flask import current_app
|
||||
import json
|
||||
|
||||
@@ -264,6 +265,7 @@ class DynamicFormBase(FlaskForm):
|
||||
'string': StringField,
|
||||
'text': TextAreaField,
|
||||
'date': DateField,
|
||||
'file': FileField,
|
||||
}.get(field_type, StringField)
|
||||
field_kwargs = {}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from flask_wtf import FlaskForm
|
||||
from wtforms import (StringField, BooleanField, SelectField, TextAreaField)
|
||||
from wtforms.fields.datetime import DateField
|
||||
from wtforms.validators import DataRequired, Length, Optional
|
||||
|
||||
from wtforms_sqlalchemy.fields import QuerySelectMultipleField
|
||||
@@ -94,3 +95,32 @@ class EditEveAITaskForm(BaseEditComponentForm):
|
||||
class EditEveAIToolForm(BaseEditComponentForm):
|
||||
pass
|
||||
|
||||
|
||||
class AddEveAIAssetForm(FlaskForm):
|
||||
name = StringField('Name', validators=[DataRequired(), Length(max=50)])
|
||||
description = TextAreaField('Description', validators=[Optional()])
|
||||
type = SelectField('Type', validators=[DataRequired()])
|
||||
valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()])
|
||||
valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
types_dict = cache_manager.assets_types_cache.get_types()
|
||||
self.type.choices = [(key, value['name']) for key, value in types_dict.items()]
|
||||
|
||||
|
||||
class EditEveAIAssetForm(FlaskForm):
|
||||
name = StringField('Name', validators=[DataRequired(), Length(max=50)])
|
||||
description = TextAreaField('Description', validators=[Optional()])
|
||||
type = SelectField('Type', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
type_version = StringField('Type Version', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
valid_from = DateField('Valid From', id='form-control datepicker', validators=[Optional()])
|
||||
valid_to = DateField('Valid To', id='form-control datepicker', validators=[Optional()])
|
||||
|
||||
|
||||
class EditEveAIAssetVersionForm(DynamicFormBase):
|
||||
asset_name = StringField('Asset Name', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
asset_type = StringField('Asset Type', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
asset_type_version = StringField('Asset Type Version', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
bucket_name = StringField('Bucket Name', validators=[DataRequired()], render_kw={'readonly': True})
|
||||
|
||||
|
||||
@@ -6,12 +6,15 @@ from flask_security import roles_accepted
|
||||
from langchain.agents import Agent
|
||||
from sqlalchemy import desc
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from werkzeug.datastructures import FileStorage
|
||||
from werkzeug.utils import secure_filename
|
||||
|
||||
from common.models.document import Embedding, DocumentVersion, Retriever
|
||||
from common.models.interaction import (ChatSession, Interaction, InteractionEmbedding, Specialist, SpecialistRetriever,
|
||||
EveAIAgent, EveAITask, EveAITool)
|
||||
EveAIAgent, EveAITask, EveAITool, EveAIAssetVersion)
|
||||
|
||||
from common.extensions import db, cache_manager
|
||||
from common.utils.asset_utils import create_asset_stack, add_asset_version_file
|
||||
from common.utils.model_logging_utils import set_logging_information, update_logging_information
|
||||
|
||||
from common.utils.middleware import mw_before_request
|
||||
@@ -22,7 +25,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)
|
||||
EditEveAIToolForm, AddEveAIAssetForm, EditEveAIAssetVersionForm)
|
||||
|
||||
interaction_bp = Blueprint('interaction_bp', __name__, url_prefix='/interaction')
|
||||
|
||||
@@ -37,6 +40,7 @@ def log_after_request(response):
|
||||
return response
|
||||
|
||||
|
||||
# Routes for Chat Session Management --------------------------------------------------------------
|
||||
@interaction_bp.before_request
|
||||
def before_request():
|
||||
try:
|
||||
@@ -88,13 +92,13 @@ def view_chat_session(chat_session_id):
|
||||
.filter_by(chat_session_id=chat_session.id)
|
||||
.join(Specialist, Interaction.specialist_id == Specialist.id, isouter=True)
|
||||
.add_columns(
|
||||
Interaction.id,
|
||||
Interaction.question_at,
|
||||
Interaction.specialist_arguments,
|
||||
Interaction.specialist_results,
|
||||
Specialist.name.label('specialist_name'),
|
||||
Specialist.type.label('specialist_type')
|
||||
).order_by(Interaction.question_at).all())
|
||||
Interaction.id,
|
||||
Interaction.question_at,
|
||||
Interaction.specialist_arguments,
|
||||
Interaction.specialist_results,
|
||||
Specialist.name.label('specialist_name'),
|
||||
Specialist.type.label('specialist_type')
|
||||
).order_by(Interaction.question_at).all())
|
||||
|
||||
# Fetch all related embeddings for the interactions in this session
|
||||
embedding_query = (db.session.query(InteractionEmbedding.interaction_id,
|
||||
@@ -129,6 +133,7 @@ def show_chat_session(chat_session):
|
||||
return render_template('interaction/view_chat_session.html', chat_session=chat_session, interactions=interactions)
|
||||
|
||||
|
||||
# Routes for Specialist Management ----------------------------------------------------------------
|
||||
@interaction_bp.route('/specialist', methods=['GET', 'POST'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def specialist():
|
||||
@@ -142,7 +147,8 @@ def specialist():
|
||||
# Populate fields individually instead of using populate_obj (gives problem with QueryMultipleSelectField)
|
||||
new_specialist.name = form.name.data
|
||||
new_specialist.type = form.type.data
|
||||
new_specialist.type_version = cache_manager.specialists_version_tree_cache.get_latest_version(new_specialist.type)
|
||||
new_specialist.type_version = cache_manager.specialists_version_tree_cache.get_latest_version(
|
||||
new_specialist.type)
|
||||
new_specialist.tuning = form.tuning.data
|
||||
|
||||
set_logging_information(new_specialist, dt.now(tz.utc))
|
||||
@@ -252,7 +258,7 @@ def edit_specialist(specialist_id):
|
||||
task_rows=task_rows,
|
||||
tool_rows=tool_rows,
|
||||
prefixed_url_for=prefixed_url_for,
|
||||
svg_path=svg_path,)
|
||||
svg_path=svg_path, )
|
||||
else:
|
||||
form_validation_failed(request, form)
|
||||
|
||||
@@ -263,7 +269,7 @@ def edit_specialist(specialist_id):
|
||||
task_rows=task_rows,
|
||||
tool_rows=tool_rows,
|
||||
prefixed_url_for=prefixed_url_for,
|
||||
svg_path=svg_path,)
|
||||
svg_path=svg_path, )
|
||||
|
||||
|
||||
@interaction_bp.route('/specialists', methods=['GET', 'POST'])
|
||||
@@ -298,7 +304,7 @@ def handle_specialist_selection():
|
||||
return redirect(prefixed_url_for('interaction_bp.specialists'))
|
||||
|
||||
|
||||
# Routes for Agent management
|
||||
# Routes for Agent management ---------------------------------------------------------------------
|
||||
@interaction_bp.route('/agent/<int:agent_id>/edit', methods=['GET'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def edit_agent(agent_id):
|
||||
@@ -338,7 +344,7 @@ def save_agent(agent_id):
|
||||
return jsonify({'success': False, 'message': 'Validation failed'})
|
||||
|
||||
|
||||
# Routes for Task management
|
||||
# Routes for Task management ----------------------------------------------------------------------
|
||||
@interaction_bp.route('/task/<int:task_id>/edit', methods=['GET'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def edit_task(task_id):
|
||||
@@ -374,7 +380,7 @@ def save_task(task_id):
|
||||
return jsonify({'success': False, 'message': 'Validation failed'})
|
||||
|
||||
|
||||
# Routes for Tool management
|
||||
# Routes for Tool management ----------------------------------------------------------------------
|
||||
@interaction_bp.route('/tool/<int:tool_id>/edit', methods=['GET'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def edit_tool(tool_id):
|
||||
@@ -410,7 +416,7 @@ def save_tool(tool_id):
|
||||
return jsonify({'success': False, 'message': 'Validation failed'})
|
||||
|
||||
|
||||
# Component selection handlers
|
||||
# Component selection handlers --------------------------------------------------------------------
|
||||
@interaction_bp.route('/handle_agent_selection', methods=['POST'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def handle_agent_selection():
|
||||
@@ -447,4 +453,93 @@ def handle_tool_selection():
|
||||
if action == "edit_tool":
|
||||
return redirect(prefixed_url_for('interaction_bp.edit_tool', tool_id=tool_id))
|
||||
|
||||
return redirect(prefixed_url_for('interaction_bp.edit_specialist'))
|
||||
return redirect(prefixed_url_for('interaction_bp.edit_specialist'))
|
||||
|
||||
|
||||
# Routes for Asset management ---------------------------------------------------------------------
|
||||
@interaction_bp.route('/add_asset', methods=['GET', 'POST'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def add_asset():
|
||||
form = AddEveAIAssetForm(request.form)
|
||||
tenant_id = session.get('tenant').get('id')
|
||||
|
||||
if form.validate_on_submit():
|
||||
try:
|
||||
current_app.logger.info(f"Adding asset for tenant {tenant_id}")
|
||||
|
||||
api_input = {
|
||||
'name': form.name.data,
|
||||
'description': form.description.data,
|
||||
'type': form.type.data,
|
||||
'valid_from': form.valid_from.data,
|
||||
'valid_to': form.valid_to.data,
|
||||
}
|
||||
new_asset, new_asset_version = create_asset_stack(api_input, tenant_id)
|
||||
|
||||
return redirect(prefixed_url_for('interaction_bp.edit_asset_version',
|
||||
asset_version_id=new_asset_version.id))
|
||||
except Exception as e:
|
||||
current_app.logger.error(f'Failed to add asset for tenant {tenant_id}: {str(e)}')
|
||||
flash('An error occurred while adding asset', 'error')
|
||||
|
||||
return render_template('interaction/add_asset.html')
|
||||
|
||||
|
||||
@interaction_bp.route('/edit_asset_version/<int:asset_version_id>', methods=['GET', 'POST'])
|
||||
@roles_accepted('Super User', 'Tenant Admin')
|
||||
def edit_asset_version(asset_version_id):
|
||||
asset_version = EveAIAssetVersion.query.get_or_404(asset_version_id)
|
||||
form = EditEveAIAssetVersionForm(asset_version)
|
||||
asset_config = cache_manager.assets_config_cache.get_config(asset_version.asset.type,
|
||||
asset_version.asset.type_version)
|
||||
configuration_config = asset_config.get('configuration')
|
||||
form.add_dynamic_fields("configuration", configuration_config, asset_version.configuration)
|
||||
|
||||
if form.validate_on_submit():
|
||||
# Update the configuration dynamic fields
|
||||
configuration = form.get_dynamic_data("configuration")
|
||||
processed_configuration = {}
|
||||
tenant_id = session.get('tenant').get('id')
|
||||
# if files are returned, we will store the file_names in the configuration, and add the file to the appropriate
|
||||
# bucket, in the appropriate location
|
||||
for field_name, field_value in configuration.items():
|
||||
# Handle file field - check if the value is a FileStorage instance
|
||||
if isinstance(field_value, FileStorage) and field_value.filename:
|
||||
try:
|
||||
# Upload file and retrieve object_name for the file
|
||||
object_name = add_asset_version_file(asset_version, field_name, field_value, tenant_id)
|
||||
|
||||
# Store object reference in configuration instead of file content
|
||||
processed_configuration[field_name] = object_name
|
||||
|
||||
except Exception as e:
|
||||
current_app.logger.error(f"Failed to upload file for asset version {asset_version.id}: {str(e)}")
|
||||
flash(f"Failed to upload file '{field_value.filename}': {str(e)}", "danger")
|
||||
return render_template('interaction/edit_asset_version.html', form=form,
|
||||
asset_version=asset_version)
|
||||
# Handle normal fields
|
||||
else:
|
||||
processed_configuration[field_name] = field_value
|
||||
|
||||
# Update the asset version with processed configuration
|
||||
asset_version.configuration = processed_configuration
|
||||
|
||||
# Update logging information
|
||||
update_logging_information(asset_version, dt.now(tz.utc))
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
flash('Asset uploaded successfully!', 'success')
|
||||
current_app.logger.info(f'Asset Version {asset_version.id} updated successfully')
|
||||
return redirect(prefixed_url_for('interaction_bp.assets'))
|
||||
except SQLAlchemyError as e:
|
||||
db.session.rollback()
|
||||
flash(f'Failed to upload asset. Error: {str(e)}', 'danger')
|
||||
current_app.logger.error(f'Failed to update asset version {asset_version.id}. Error: {str(e)}')
|
||||
return render_template('interaction/edit_asset_version.html', form=form)
|
||||
else:
|
||||
form_validation_failed(request, form)
|
||||
|
||||
return render_template('interaction/edit_asset_version.html', form=form)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user