- Introducing translation service prompts

- Ensure Traicie Role Definition Specialist complies to latest technical requirements
- Ensure that empty historical messages do not cause a crash in eveai_client
- take into account empty customisation options
- make was not processed in the system dynamic attribute tenant_make
- ensure only relevant makes are shown when creating magic links
- refresh partner info when editing or adding Partner Services$
This commit is contained in:
Josako
2025-06-24 14:15:36 +02:00
parent 043cea45f2
commit f5c9542a49
15 changed files with 147 additions and 32 deletions

View File

@@ -317,3 +317,27 @@ class SpecialistMagicLinkTenant(db.Model):
magic_link_code = db.Column(db.String(55), primary_key=True) magic_link_code = db.Column(db.String(55), primary_key=True)
tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False) tenant_id = db.Column(db.Integer, db.ForeignKey('public.tenant.id'), nullable=False)
class TranslationCache(db.Model):
__bind_key__ = 'public'
__table_args__ = {'schema': 'public'}
cache_key = db.Column(db.String(16), primary_key=True)
source_text = db.Column(db.Text, nullable=False)
translated_text = db.Column(db.Text, nullable=False)
source_language = db.Column(db.String(2), nullable=False)
target_language = db.Column(db.String(2), nullable=False)
context = db.Column(db.Text, nullable=True)
# Translation cost
input_tokens = db.Column(db.Integer, nullable=False)
output_tokens = db.Column(db.Integer, nullable=False)
# Tracking
created_at = db.Column(db.DateTime, nullable=False, server_default=db.func.now())
created_by = db.Column(db.Integer, db.ForeignKey('public.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('public.user.id'), nullable=True)
last_used_at = db.Column(db.DateTime, nullable=True)

View File

@@ -1,14 +1,18 @@
import json
""" """
Utility functions for chat customization. Utility functions for chat customization.
""" """
from flask import current_app
def get_default_chat_customisation(tenant_customisation=None): def get_default_chat_customisation(tenant_customisation=None):
""" """
Get chat customization options with default values for missing options. Get chat customization options with default values for missing options.
Args: Args:
tenant_customization (dict, optional): The tenant's customization options. tenant_customisation (dict or str, optional): The tenant's customization options.
Defaults to None. Defaults to None. Can be a dict or a JSON string.
Returns: Returns:
dict: A dictionary containing all customization options with default values dict: A dictionary containing all customization options with default values
@@ -37,9 +41,20 @@ def get_default_chat_customisation(tenant_customisation=None):
# Start with the default customization # Start with the default customization
customisation = default_customisation.copy() customisation = default_customisation.copy()
# Convert JSON string to dict if needed
if isinstance(tenant_customisation, str):
try:
tenant_customisation = json.loads(tenant_customisation)
current_app.logger.debug(f"Converted JSON string to dict: {tenant_customisation}")
except json.JSONDecodeError as e:
current_app.logger.error(f"Error parsing JSON customisation: {e}")
return default_customisation
# Update with tenant customization # Update with tenant customization
for key, value in tenant_customisation.items(): current_app.logger.debug(f"Tenant customisation - in default creation: {tenant_customisation}")
if key in customisation: if tenant_customisation:
customisation[key] = value for key, value in tenant_customisation.items():
if key in customisation:
customisation[key] = value
return customisation return customisation

View File

@@ -0,0 +1,21 @@
import xxhash
import json
from common.utils.model_utils import get_template, replace_variable_in_template
def generate_cache_key(text: str, target_lang: str, source_lang: str = None, context: str = None) -> str:
cache_data = {
"text": text.strip(),
"target_lang": target_lang.lower(),
"source_lang": source_lang.lower() if source_lang else None,
"context": context.strip() if context else ""
}
cache_string = json.dumps(cache_data, sort_keys=True, ensure_ascii=False)
return xxhash.xxh64(cache_string.encode('utf-8')).hexdigest()
def translate_text(text: str, target_lang: str, source_lang: str = None, context: str = None) -> str:
if context:
prompt_text = get_template("translation_with_context")
prompt_text = replace_variable_in_template(prompt_text, "context", context)

View File

@@ -148,7 +148,7 @@ class Config(object):
}, },
} }
SUPPORTED_LANGUAGES_Full = list(SUPPORTED_LANGUAGE_DETAILS.keys()) SUPPORTED_LANGUAGES_FULL = list(SUPPORTED_LANGUAGE_DETAILS.keys())
# supported currencies # supported currencies
SUPPORTED_CURRENCIES = ['', '$'] SUPPORTED_CURRENCIES = ['', '$']
@@ -156,10 +156,7 @@ class Config(object):
# supported LLMs # supported LLMs
# SUPPORTED_EMBEDDINGS = ['openai.text-embedding-3-small', 'openai.text-embedding-3-large', 'mistral.mistral-embed'] # SUPPORTED_EMBEDDINGS = ['openai.text-embedding-3-small', 'openai.text-embedding-3-large', 'mistral.mistral-embed']
SUPPORTED_EMBEDDINGS = ['mistral.mistral-embed'] SUPPORTED_EMBEDDINGS = ['mistral.mistral-embed']
SUPPORTED_LLMS = ['openai.gpt-4o', 'openai.gpt-4o-mini', SUPPORTED_LLMS = ['mistral.mistral-large-latest', 'mistral.mistral-medium_latest', 'mistral.mistral-small-latest']
'mistral.mistral-large-latest', 'mistral.mistral-medium_latest', 'mistral.mistral-small-latest']
ANTHROPIC_LLM_VERSIONS = {'claude-3-5-sonnet': 'claude-3-5-sonnet-20240620', }
# Annotation text chunk length # Annotation text chunk length
ANNOTATION_TEXT_CHUNK_LENGTH = 10000 ANNOTATION_TEXT_CHUNK_LENGTH = 10000

View File

@@ -0,0 +1,15 @@
version: "1.0.0"
content: >
You are a top translator. We need you to translate {text_to_translate} into {target_language}, taking into account
this context:
{context}
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
without further interpretation. If more than one option is available, present me with the most probable one.
metadata:
author: "Josako"
date_added: "2025-06-23"
description: "An assistant to translate given a context."
changes: "Initial version"

View File

@@ -0,0 +1,12 @@
version: "1.0.0"
content: >
You are a top translator. We need you to translate {text_to_translate} into {target_language}.
I only want you to return the translation. No explanation, no options. I need to be able to directly use your answer
without further interpretation. If more than one option is available, present me with the most probable one.
metadata:
author: "Josako"
date_added: "2025-06-23"
description: "An assistant to translate without context."
changes: "Initial version"

View File

@@ -28,4 +28,12 @@ PROMPT_TYPES = {
"name": "transcript", "name": "transcript",
"description": "An assistant to transform a transcript to markdown.", "description": "An assistant to transform a transcript to markdown.",
}, },
"translation_with_context": {
"name": "translation_with_context",
"description": "An assistant to translate text with context",
},
"translation_without_context": {
"name": "translation_without_context",
"description": "An assistant to translate text without context",
},
} }

View File

@@ -312,7 +312,7 @@ class DynamicFormBase(FlaskForm):
field_class = SelectField field_class = SelectField
tenant_id = session.get('tenant').get('id') tenant_id = session.get('tenant').get('id')
makes = TenantMake.query.filter_by(tenant_id=tenant_id).all() makes = TenantMake.query.filter_by(tenant_id=tenant_id).all()
choices = [(make.name, make.name) for make in makes] choices = [(make.id, make.name) for make in makes]
extra_classes = '' extra_classes = ''
field_kwargs = {'choices': choices} field_kwargs = {'choices': choices}
@@ -581,7 +581,10 @@ class DynamicFormBase(FlaskForm):
except Exception as e: except Exception as e:
current_app.logger.error(f"Error converting initial data to patterns: {e}") current_app.logger.error(f"Error converting initial data to patterns: {e}")
elif isinstance(field, DateField): elif isinstance(field, DateField):
data[original_field_name] = field.data.isoformat() if field.data:
data[original_field_name] = field.data.isoformat()
else:
data[original_field_name] = None
else: else:
data[original_field_name] = field.data data[original_field_name] = field.data
return data return data

View File

@@ -1,3 +1,4 @@
from flask import session
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import (StringField, BooleanField, SelectField, TextAreaField) from wtforms import (StringField, BooleanField, SelectField, TextAreaField)
from wtforms.fields.datetime import DateField from wtforms.fields.datetime import DateField
@@ -181,7 +182,8 @@ class EditSpecialistMagicLinkForm(DynamicFormBase):
self.specialist_name.data = '' self.specialist_name.data = ''
# Dynamically populate the tenant_make field with None as first option # Dynamically populate the tenant_make field with None as first option
tenant_makes = TenantMake.query.all() tenant_id = session.get('tenant').get('id')
tenant_makes = TenantMake.query.filter_by(tenant_id=tenant_id).all()
self.tenant_make_id.choices = [(0, 'None')] + [(make.id, make.name) for make in tenant_makes] self.tenant_make_id.choices = [(0, 'None')] + [(make.id, make.name) for make in tenant_makes]

View File

@@ -702,12 +702,13 @@ def specialist_magic_link():
new_spec_ml_tenant.tenant_id = tenant_id new_spec_ml_tenant.tenant_id = tenant_id
# Define the make valid for this magic link # Define the make valid for this magic link
make_id = SpecialistServices.get_specialist_system_field(new_specialist_magic_link.specialist_id, specialist = Specialist.query.get(new_specialist_magic_link.specialist_id)
"make", "tenant_make") make_id = specialist.configuration.get('make', None)
current_app.logger.debug(f"make_id defined in specialist: {make_id}")
if make_id: if make_id:
new_spec_ml_tenant.tenant_make_id = make_id new_specialist_magic_link.tenant_make_id = make_id
elif session.get('tenant').get('default_tenant_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') new_specialist_magic_link.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

@@ -62,6 +62,7 @@ def edit_partner(partner_id):
update_logging_information(partner, dt.now(tz.utc)) update_logging_information(partner, dt.now(tz.utc))
db.session.commit() db.session.commit()
flash('Partner updated successfully.', 'success') flash('Partner updated successfully.', 'success')
refresh_session_partner(partner.id)
return redirect( return redirect(
prefixed_url_for('partner_bp.edit_partner', prefixed_url_for('partner_bp.edit_partner',
partner_id=partner.id)) # Assuming there's a user profile view to redirect to partner_id=partner.id)) # Assuming there's a user profile view to redirect to
@@ -197,6 +198,7 @@ def edit_partner_service(partner_service_id):
db.session.commit() db.session.commit()
flash('Partner Service updated successfully.', 'success') flash('Partner Service updated successfully.', 'success')
current_app.logger.info(f"Partner Service {partner_service.name} updated successfully! ") current_app.logger.info(f"Partner Service {partner_service.name} updated successfully! ")
refresh_session_partner(partner_id)
except SQLAlchemyError as e: except SQLAlchemyError as e:
db.session.rollback() db.session.rollback()
flash(f'Failed to update Partner Service: {str(e)}', 'danger') flash(f'Failed to update Partner Service: {str(e)}', 'danger')
@@ -339,4 +341,7 @@ def add_partner_service_for_tenant(partner_service_id):
return redirect(prefixed_url_for('partner_bp.partner_services')) return redirect(prefixed_url_for('partner_bp.partner_services'))
def refresh_session_partner(partner_id):
if session.get('partner', None):
if partner_id == session['partner']['id']:
session['partner'] = Partner.query.get_or_404(partner_id).to_dict()

View File

@@ -131,18 +131,20 @@ export const ChatApp = {
const historicalMessages = chatConfig.messages || []; const historicalMessages = chatConfig.messages || [];
if (historicalMessages.length > 0) { if (historicalMessages.length > 0) {
this.allMessages = historicalMessages.map(msg => { this.allMessages = historicalMessages
// Zorg voor een correct geformatteerde bericht-object .filter(msg => msg !== null && msg !== undefined) // Filter null/undefined berichten uit
return { .map(msg => {
id: this.messageIdCounter++, // Zorg voor een correct geformatteerde bericht-object
content: typeof msg === 'string' ? msg : msg.content || '', return {
sender: msg.sender || 'ai', id: this.messageIdCounter++,
type: msg.type || 'text', content: typeof msg === 'string' ? msg : (msg.content || ''),
timestamp: msg.timestamp || new Date().toISOString(), sender: msg.sender || 'ai',
formData: msg.formData || null, type: msg.type || 'text',
status: msg.status || 'delivered' timestamp: msg.timestamp || new Date().toISOString(),
}; formData: msg.formData || null,
}); status: msg.status || 'delivered'
};
});
console.log(`Loaded ${this.allMessages.length} historical messages`); console.log(`Loaded ${this.allMessages.length} historical messages`);
} }

View File

@@ -86,7 +86,13 @@ def chat(magic_link_code):
session['chat_session_id'] = SpecialistServices.start_session() session['chat_session_id'] = SpecialistServices.start_session()
# Get customisation options with defaults # Get customisation options with defaults
customisation = get_default_chat_customisation(tenant_make.chat_customisation_options) current_app.logger.debug(f"Make Customisation Options: {tenant_make.chat_customisation_options}")
try:
customisation = get_default_chat_customisation(tenant_make.chat_customisation_options)
except Exception as e:
current_app.logger.error(f"Error processing customisation options: {str(e)}")
# Fallback to default customisation
customisation = get_default_chat_customisation(None)
# Start a new chat session # Start a new chat session
session['chat_session_id'] = SpecialistServices.start_session() session['chat_session_id'] = SpecialistServices.start_session()

View File

@@ -69,6 +69,9 @@ class SpecialistExecutor(CrewAIBaseSpecialistExecutor):
self.role_definition_crew self.role_definition_crew
) )
def _config_state_result_relations(self):
pass
def execute(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult: def execute(self, arguments: SpecialistArguments, formatted_context, citations) -> SpecialistResult:
self.log_tuning("Traicie Role Definition Specialist execution started", {}) self.log_tuning("Traicie Role Definition Specialist execution started", {})

View File

@@ -94,4 +94,5 @@ scaleway~=2.9.0
html2text~=2025.4.15 html2text~=2025.4.15
markdown~=3.8 markdown~=3.8
python-json-logger~=2.0.7 python-json-logger~=2.0.7
qrcode[pil]==8.2 qrcode[pil]==8.2
xxhash~=3.5.0