- Introduction of TRACIE_KO_INTERVIEW_DEFINITION_SPECIALIST
- Re-introduction of EveAIAsset - Make translation services resistent for situation with and without current_event defined. - Ensure first question is asked in eveai_chat_client - Start of version 1.4.0 of TRAICIE_SELECTION_SPECIALIST
This commit is contained in:
@@ -67,25 +67,23 @@ class EveAIAsset(db.Model):
|
||||
description = db.Column(db.Text, nullable=True)
|
||||
type = db.Column(db.String(50), nullable=False, default="DOCUMENT_TEMPLATE")
|
||||
type_version = db.Column(db.String(20), nullable=True, default="1.0.0")
|
||||
valid_from = db.Column(db.DateTime, nullable=True)
|
||||
valid_to = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
# Versioning Information
|
||||
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
|
||||
created_by = db.Column(db.Integer, db.ForeignKey(User.id), nullable=True)
|
||||
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))
|
||||
|
||||
# Relations
|
||||
versions = db.relationship('EveAIAssetVersion', backref='asset', lazy=True)
|
||||
|
||||
|
||||
class EveAIAssetVersion(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
asset_id = db.Column(db.Integer, db.ForeignKey(EveAIAsset.id), nullable=False)
|
||||
# Storage information
|
||||
bucket_name = db.Column(db.String(255), nullable=True)
|
||||
object_name = db.Column(db.String(200), nullable=True)
|
||||
file_type = db.Column(db.String(20), nullable=True)
|
||||
file_size = db.Column(db.Float, nullable=True)
|
||||
|
||||
# Metadata information
|
||||
user_metadata = db.Column(JSONB, nullable=True)
|
||||
system_metadata = db.Column(JSONB, nullable=True)
|
||||
|
||||
# Configuration information
|
||||
configuration = db.Column(JSONB, nullable=True)
|
||||
arguments = db.Column(JSONB, nullable=True)
|
||||
|
||||
# Cost information
|
||||
prompt_tokens = db.Column(db.Integer, nullable=True)
|
||||
completion_tokens = db.Column(db.Integer, nullable=True)
|
||||
|
||||
# Versioning Information
|
||||
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
|
||||
@@ -93,25 +91,7 @@ class EveAIAssetVersion(db.Model):
|
||||
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))
|
||||
|
||||
# Relations
|
||||
instructions = db.relationship('EveAIAssetInstruction', backref='asset_version', lazy=True)
|
||||
|
||||
|
||||
class EveAIAssetInstruction(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
asset_version_id = db.Column(db.Integer, db.ForeignKey(EveAIAssetVersion.id), nullable=False)
|
||||
name = db.Column(db.String(255), nullable=False)
|
||||
content = db.Column(db.Text, nullable=True)
|
||||
|
||||
|
||||
class EveAIProcessedAsset(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
asset_version_id = db.Column(db.Integer, db.ForeignKey(EveAIAssetVersion.id), nullable=False)
|
||||
specialist_id = db.Column(db.Integer, db.ForeignKey(Specialist.id), nullable=True)
|
||||
chat_session_id = db.Column(db.Integer, db.ForeignKey(ChatSession.id), nullable=True)
|
||||
bucket_name = db.Column(db.String(255), nullable=True)
|
||||
object_name = db.Column(db.String(255), nullable=True)
|
||||
created_at = db.Column(db.DateTime, nullable=True, server_default=db.func.now())
|
||||
last_used_at = db.Column(db.DateTime, nullable=True)
|
||||
|
||||
|
||||
class EveAIAgent(db.Model):
|
||||
|
||||
9
common/services/interaction/asset_services.py
Normal file
9
common/services/interaction/asset_services.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from common.models.interaction import EveAIAsset
|
||||
from common.extensions import minio_client
|
||||
|
||||
|
||||
class AssetServices:
|
||||
|
||||
@staticmethod
|
||||
def add_or_replace_asset_file(asset_id, file_data):
|
||||
asset = EveAIAsset.query.get_or_404(asset_id)
|
||||
65
common/services/utils/answer_check_services.py
Normal file
65
common/services/utils/answer_check_services.py
Normal file
@@ -0,0 +1,65 @@
|
||||
from flask import current_app, session
|
||||
from langchain_core.output_parsers import StrOutputParser
|
||||
from langchain_core.prompts import ChatPromptTemplate
|
||||
from langchain_core.runnables import RunnablePassthrough
|
||||
|
||||
from common.utils.business_event import BusinessEvent
|
||||
from common.utils.business_event_context import current_event
|
||||
from common.utils.model_utils import get_template
|
||||
from eveai_chat_workers.outputs.globals.q_a_output.q_a_output_v1_0 import QAOutput
|
||||
|
||||
|
||||
class AnswerCheckServices:
|
||||
@staticmethod
|
||||
def check_affirmative_answer(question: str, answer: str, language_iso: str) -> bool:
|
||||
return AnswerCheckServices._check_answer(question, answer, language_iso, "check_affirmative_answer",
|
||||
"Check Affirmative Answer")
|
||||
|
||||
@staticmethod
|
||||
def check_additional_information(question: str, answer: str, language_iso: str) -> bool:
|
||||
return AnswerCheckServices._check_answer(question, answer, language_iso, "check_additional_information",
|
||||
"Check Additional Information")
|
||||
|
||||
@staticmethod
|
||||
def _check_answer(question: str, answer: str, language_iso: str, template_name: str, span_name: str) -> bool:
|
||||
if language_iso.strip() == '':
|
||||
raise ValueError("Language cannot be empty")
|
||||
language = current_app.config.get('SUPPORTED_LANGUAGE_ISO639_1_LOOKUP').get(language_iso)
|
||||
if language is None:
|
||||
raise ValueError(f"Unsupported language: {language_iso}")
|
||||
if question.strip() == '':
|
||||
raise ValueError("Question cannot be empty")
|
||||
if answer.strip() == '':
|
||||
raise ValueError("Answer cannot be empty")
|
||||
|
||||
tenant_id = session.get('tenant').get('id')
|
||||
|
||||
if not current_event:
|
||||
with BusinessEvent('Answer Check Service', tenant_id):
|
||||
with current_event.create_span(span_name):
|
||||
return AnswerCheckServices._check_answer_logic(question, answer, language, template_name)
|
||||
else:
|
||||
with current_event.create_span('Check Affirmative Answer'):
|
||||
return AnswerCheckServices._check_answer_logic(question, answer, language, template_name)
|
||||
|
||||
@staticmethod
|
||||
def _check_answer_logic(question: str, answer: str, language: str, template_name: str) -> bool:
|
||||
prompt_params = {
|
||||
'question': question,
|
||||
'answer': answer,
|
||||
'language': language,
|
||||
}
|
||||
|
||||
template, llm = get_template(template_name)
|
||||
check_answer_prompt = ChatPromptTemplate.from_template(template)
|
||||
setup = RunnablePassthrough()
|
||||
|
||||
output_schema = QAOutput
|
||||
structured_llm = llm.with_structured_output(output_schema)
|
||||
|
||||
chain = (setup | check_answer_prompt | structured_llm )
|
||||
|
||||
raw_answer = chain.invoke(prompt_params)
|
||||
current_app.logger.debug(f"Raw answer: {raw_answer}")
|
||||
|
||||
return raw_answer.answer
|
||||
@@ -1,5 +1,8 @@
|
||||
import json
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
from flask import session
|
||||
|
||||
from common.extensions import cache_manager
|
||||
from common.utils.business_event import BusinessEvent
|
||||
from common.utils.business_event_context import current_event
|
||||
@@ -7,11 +10,13 @@ from common.utils.business_event_context import current_event
|
||||
class TranslationServices:
|
||||
|
||||
@staticmethod
|
||||
def translate_config(config_data: Dict[str, Any], field_config: str, target_language: str, source_language: Optional[str] = None, context: Optional[str] = None) -> Dict[str, Any]:
|
||||
def translate_config(tenant_id: int, config_data: Dict[str, Any], field_config: str, target_language: str,
|
||||
source_language: Optional[str] = None, context: Optional[str] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
Vertaalt een configuratie op basis van een veld-configuratie.
|
||||
|
||||
Args:
|
||||
tenant_id: Identificatie van de tenant waarvoor we de vertaling doen.
|
||||
config_data: Een dictionary of JSON (die dan wordt geconverteerd naar een dictionary) met configuratiegegevens
|
||||
field_config: De naam van een veld-configuratie (bijv. 'fields')
|
||||
target_language: De taal waarnaar vertaald moet worden
|
||||
@@ -21,6 +26,26 @@ class TranslationServices:
|
||||
Returns:
|
||||
Een dictionary met de vertaalde configuratie
|
||||
"""
|
||||
config_type = config_data.get('type', 'Unknown')
|
||||
config_version = config_data.get('version', 'Unknown')
|
||||
span_name = f"{config_type}-{config_version}-{field_config}"
|
||||
|
||||
if current_event:
|
||||
with current_event.create_span(span_name):
|
||||
translated_config = TranslationServices._translate_config(tenant_id, config_data, field_config,
|
||||
target_language, source_language, context)
|
||||
return translated_config
|
||||
else:
|
||||
with BusinessEvent('Config Translation Service', tenant_id):
|
||||
with current_event.create_span(span_name):
|
||||
translated_config = TranslationServices._translate_config(tenant_id, config_data, field_config,
|
||||
target_language, source_language, context)
|
||||
return translated_config
|
||||
|
||||
@staticmethod
|
||||
def _translate_config(tenant_id: int, config_data: Dict[str, Any], field_config: str, target_language: str,
|
||||
source_language: Optional[str] = None, context: Optional[str] = None) -> Dict[str, Any]:
|
||||
|
||||
# Zorg ervoor dat we een dictionary hebben
|
||||
if isinstance(config_data, str):
|
||||
config_data = json.loads(config_data)
|
||||
@@ -31,78 +56,79 @@ class TranslationServices:
|
||||
# Haal type en versie op voor de Business Event span
|
||||
config_type = config_data.get('type', 'Unknown')
|
||||
config_version = config_data.get('version', 'Unknown')
|
||||
span_name = f"{config_type}-{config_version}-{field_config}"
|
||||
|
||||
# Start een Business Event context
|
||||
with BusinessEvent('Config Translation Service', 0):
|
||||
with current_event.create_span(span_name):
|
||||
# Controleer of de gevraagde veld-configuratie bestaat
|
||||
if field_config in config_data:
|
||||
fields = config_data[field_config]
|
||||
if field_config in config_data:
|
||||
fields = config_data[field_config]
|
||||
|
||||
# Haal description uit metadata voor context als geen context is opgegeven
|
||||
description_context = ""
|
||||
if not context and 'metadata' in config_data and 'description' in config_data['metadata']:
|
||||
description_context = config_data['metadata']['description']
|
||||
# Haal description uit metadata voor context als geen context is opgegeven
|
||||
description_context = ""
|
||||
if not context and 'metadata' in config_data and 'description' in config_data['metadata']:
|
||||
description_context = config_data['metadata']['description']
|
||||
|
||||
# Loop door elk veld in de configuratie
|
||||
for field_name, field_data in fields.items():
|
||||
# Vertaal name als het bestaat en niet leeg is
|
||||
if 'name' in field_data and field_data['name']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_name = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['name'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_name:
|
||||
translated_config[field_config][field_name]['name'] = translated_name.translated_text
|
||||
# Loop door elk veld in de configuratie
|
||||
for field_name, field_data in fields.items():
|
||||
# Vertaal name als het bestaat en niet leeg is
|
||||
if 'name' in field_data and field_data['name']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_name = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['name'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_name:
|
||||
translated_config[field_config][field_name]['name'] = translated_name.translated_text
|
||||
|
||||
if 'title' in field_data and field_data['title']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_title = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['title'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_title:
|
||||
translated_config[field_config][field_name]['title'] = translated_title.translated_text
|
||||
if 'title' in field_data and field_data['title']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_title = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['title'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_title:
|
||||
translated_config[field_config][field_name]['title'] = translated_title.translated_text
|
||||
|
||||
# Vertaal description als het bestaat en niet leeg is
|
||||
if 'description' in field_data and field_data['description']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_desc = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['description'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_desc:
|
||||
translated_config[field_config][field_name]['description'] = translated_desc.translated_text
|
||||
# Vertaal description als het bestaat en niet leeg is
|
||||
if 'description' in field_data and field_data['description']:
|
||||
# Gebruik context indien opgegeven, anders description_context
|
||||
field_context = context if context else description_context
|
||||
translated_desc = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['description'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=field_context
|
||||
)
|
||||
if translated_desc:
|
||||
translated_config[field_config][field_name]['description'] = translated_desc.translated_text
|
||||
|
||||
# Vertaal context als het bestaat en niet leeg is
|
||||
if 'context' in field_data and field_data['context']:
|
||||
translated_ctx = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['context'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=context
|
||||
)
|
||||
if translated_ctx:
|
||||
translated_config[field_config][field_name]['context'] = translated_ctx.translated_text
|
||||
# Vertaal context als het bestaat en niet leeg is
|
||||
if 'context' in field_data and field_data['context']:
|
||||
translated_ctx = cache_manager.translation_cache.get_translation(
|
||||
text=field_data['context'],
|
||||
target_lang=target_language,
|
||||
source_lang=source_language,
|
||||
context=context
|
||||
)
|
||||
if translated_ctx:
|
||||
translated_config[field_config][field_name]['context'] = translated_ctx.translated_text
|
||||
|
||||
return translated_config
|
||||
return translated_config
|
||||
|
||||
@staticmethod
|
||||
def translate(text: str, target_language: str, source_language: Optional[str] = None,
|
||||
def translate(tenant_id: int, text: str, target_language: str, source_language: Optional[str] = None,
|
||||
context: Optional[str] = None)-> str:
|
||||
with BusinessEvent('Translation Service', 0):
|
||||
if current_event:
|
||||
with current_event.create_span('Translation'):
|
||||
translation_cache = cache_manager.translation_cache.get_translation(text, target_language,
|
||||
source_language, context)
|
||||
return translation_cache.translated_text
|
||||
return translation_cache.translated_text
|
||||
else:
|
||||
with BusinessEvent('Translation Service', tenant_id):
|
||||
with current_event.create_span('Translation'):
|
||||
translation_cache = cache_manager.translation_cache.get_translation(text, target_language,
|
||||
source_language, context)
|
||||
return translation_cache.translated_text
|
||||
@@ -4,59 +4,9 @@ from flask import current_app
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
|
||||
from common.extensions import cache_manager, minio_client, db
|
||||
from common.models.interaction import EveAIAsset, EveAIAssetVersion
|
||||
from common.models.interaction import EveAIAsset
|
||||
from common.utils.model_logging_utils import set_logging_information
|
||||
|
||||
|
||||
def create_asset_stack(api_input, tenant_id):
|
||||
type_version = cache_manager.assets_version_tree_cache.get_latest_version(api_input['type'])
|
||||
api_input['type_version'] = type_version
|
||||
new_asset = create_asset(api_input, tenant_id)
|
||||
new_asset_version = create_version_for_asset(new_asset, tenant_id)
|
||||
db.session.add(new_asset)
|
||||
db.session.add(new_asset_version)
|
||||
|
||||
try:
|
||||
db.session.commit()
|
||||
except SQLAlchemyError as e:
|
||||
current_app.logger.error(f"Could not add asset for tenant {tenant_id}: {str(e)}")
|
||||
db.session.rollback()
|
||||
raise e
|
||||
|
||||
return new_asset, new_asset_version
|
||||
|
||||
|
||||
def create_asset(api_input, tenant_id):
|
||||
new_asset = EveAIAsset()
|
||||
new_asset.name = api_input['name']
|
||||
new_asset.description = api_input['description']
|
||||
new_asset.type = api_input['type']
|
||||
new_asset.type_version = api_input['type_version']
|
||||
if api_input['valid_from'] and api_input['valid_from'] != '':
|
||||
new_asset.valid_from = api_input['valid_from']
|
||||
else:
|
||||
new_asset.valid_from = dt.now(tz.utc)
|
||||
new_asset.valid_to = api_input['valid_to']
|
||||
set_logging_information(new_asset, dt.now(tz.utc))
|
||||
|
||||
return new_asset
|
||||
|
||||
|
||||
def create_version_for_asset(asset, tenant_id):
|
||||
new_asset_version = EveAIAssetVersion()
|
||||
new_asset_version.asset = asset
|
||||
new_asset_version.bucket_name = minio_client.create_tenant_bucket(tenant_id)
|
||||
set_logging_information(new_asset_version, dt.now(tz.utc))
|
||||
|
||||
return new_asset_version
|
||||
|
||||
|
||||
def add_asset_version_file(asset_version, field_name, file, tenant_id):
|
||||
object_name, file_size = minio_client.upload_file(asset_version.bucket_name, asset_version.id, field_name,
|
||||
file.content_type)
|
||||
# mark_tenant_storage_dirty(tenant_id)
|
||||
# TODO - zorg ervoor dat de herberekening van storage onmiddellijk gebeurt!
|
||||
return object_name
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -33,8 +33,8 @@ class MinioClient:
|
||||
def generate_object_name(self, document_id, language, version_id, filename):
|
||||
return f"{document_id}/{language}/{version_id}/{filename}"
|
||||
|
||||
def generate_asset_name(self, asset_version_id, file_name, content_type):
|
||||
return f"assets/{asset_version_id}/{file_name}.{content_type}"
|
||||
def generate_asset_name(self, asset_id, asset_type, content_type):
|
||||
return f"assets/{asset_type}/{asset_id}.{content_type}"
|
||||
|
||||
def upload_document_file(self, tenant_id, document_id, language, version_id, filename, file_data):
|
||||
bucket_name = self.generate_bucket_name(tenant_id)
|
||||
@@ -57,8 +57,10 @@ class MinioClient:
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while uploading file: {err}")
|
||||
|
||||
def upload_asset_file(self, bucket_name, asset_version_id, file_name, file_type, file_data):
|
||||
object_name = self.generate_asset_name(asset_version_id, file_name, file_type)
|
||||
def upload_asset_file(self, tenant_id: int, asset_id: int, asset_type: str, file_type: str,
|
||||
file_data: bytes | FileStorage | io.BytesIO | str,) -> tuple[str, str, int]:
|
||||
bucket_name = self.generate_bucket_name(tenant_id)
|
||||
object_name = self.generate_asset_name(asset_id, asset_type, file_type)
|
||||
|
||||
try:
|
||||
if isinstance(file_data, FileStorage):
|
||||
@@ -73,7 +75,7 @@ class MinioClient:
|
||||
self.client.put_object(
|
||||
bucket_name, object_name, io.BytesIO(file_data), len(file_data)
|
||||
)
|
||||
return object_name, len(file_data)
|
||||
return bucket_name, object_name, len(file_data)
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while uploading asset: {err}")
|
||||
|
||||
@@ -84,6 +86,13 @@ class MinioClient:
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while downloading file: {err}")
|
||||
|
||||
def download_asset_file(self, tenant_id, bucket_name, object_name):
|
||||
try:
|
||||
response = self.client.get_object(bucket_name, object_name)
|
||||
return response.read()
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while downloading asset: {err}")
|
||||
|
||||
def list_document_files(self, tenant_id, document_id, language=None, version_id=None):
|
||||
bucket_name = self.generate_bucket_name(tenant_id)
|
||||
prefix = f"{document_id}/"
|
||||
@@ -105,3 +114,9 @@ class MinioClient:
|
||||
return True
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while deleting file: {err}")
|
||||
|
||||
def delete_object(self, bucket_name, object_name):
|
||||
try:
|
||||
self.client.remove_object(bucket_name, object_name)
|
||||
except S3Error as err:
|
||||
raise Exception(f"Error occurred while deleting object: {err}")
|
||||
Reference in New Issue
Block a user