Add utility modules and configuration settings for chatbot application

This commit is contained in:
2025-03-08 18:12:18 +01:00
parent d4518a89dd
commit cb43b1176f
16 changed files with 1237 additions and 0 deletions

1
utils/__init__.py Normal file
View File

@@ -0,0 +1 @@
# Package initialization for display utilities

15
utils/conversion.py Normal file
View File

@@ -0,0 +1,15 @@
import base64
from io import BytesIO
from PIL import Image
def base64_to_image(base64_data):
"""Convertit une image base64 en objet Image pour l'affichage direct"""
try:
if not base64_data:
return None
image_bytes = base64.b64decode(base64_data)
image = Image.open(BytesIO(image_bytes))
return image
except Exception as e:
print(f"Erreur lors de la conversion d'image: {e}")
return None

40
utils/display.py Normal file
View File

@@ -0,0 +1,40 @@
from PIL import Image
from io import BytesIO
import base64
def base64_to_image(base64_data):
"""Convert base64 image data to PIL Image"""
try:
if not base64_data:
return None
image_bytes = base64.b64decode(base64_data)
return Image.open(BytesIO(image_bytes))
except Exception as e:
print(f"Image conversion error: {e}")
return None
def display_images(current_images):
"""Format images for Gradio gallery display"""
if not current_images:
return None
return [
(img["image"], f"{img['caption']} (Source: {img['source']}, Page: {img['page']})")
for img in current_images
if img.get("image")
]
def display_tables(current_tables):
"""Format tables for HTML display"""
if not current_tables:
return None
html = ""
for table in current_tables:
html += f"""
<div style="margin-bottom: 20px; border: 1px solid #ddd; padding: 15px; border-radius: 8px;">
<h3>{table['caption']}</h3>
<p style="color:#666; font-size:0.9em;">Source: {table['source']}, Page: {table['page']}</p>
<div class="table-container">{table.get('data', '')}</div>
</div>
"""
return html if html else None

29
utils/image_utils.py Normal file
View File

@@ -0,0 +1,29 @@
from io import BytesIO
from PIL import Image
import base64
def base64_to_image(base64_data):
"""Convertit une image base64 en objet Image pour l'affichage direct"""
try:
if not base64_data:
return None
image_bytes = base64.b64decode(base64_data)
image = Image.open(BytesIO(image_bytes))
return image
except Exception as e:
print(f"Erreur lors de la conversion d'image: {e}")
return None
def display_images(current_images):
"""Prépare les images pour l'affichage dans la galerie Gradio"""
if not current_images:
return None
gallery = []
for img_data in current_images:
image = img_data["image"]
if image:
caption = f"{img_data['caption']} (Source: {img_data['source']}, Page: {img_data['page']})"
gallery.append((image, caption))
return gallery if gallery else None

190
utils/katex_script.py Normal file
View File

@@ -0,0 +1,190 @@
KATEX_CSS_JS = """
<style>
.gradio-container {max-width: 1200px !important}
#chatbot {height: 600px; overflow-y: auto;}
#sources_info {margin-top: 10px; color: #666;}
/* Improved styles for equations */
.katex { font-size: 1.1em !important; }
.math-inline { background: #f8f9fa; padding: 2px 5px; border-radius: 4px; }
.math-display { background: #f8f9f9; margin: 10px 0; padding: 10px; border-radius: 5px; overflow-x: auto; text-align: center; }
/* Table styles */
table {
border-collapse: collapse;
width: 100%;
margin: 15px 0;
font-size: 0.9em;
}
table, th, td {
border: 1px solid #ddd;
}
th, td {
padding: 8px 12px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
.table-container {
overflow-x: auto;
margin-top: 10px;
}
</style>
<!-- Loading KaTeX -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.css">
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.8/dist/contrib/auto-render.min.js"></script>
<script>
// Function to process math equations with KaTeX
function renderMathInElement(element) {
if (!window.renderMathInElement) return;
try {
window.renderMathInElement(element, {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '$', right: '$', display: false},
{left: '\\\\(', right: '\\\\)', display: false},
{left: '\\\\[', right: '\\\\]', display: true}
],
throwOnError: false,
trust: true,
strict: false,
macros: {
"\\\\R": "\\\\mathbb{R}",
"\\\\N": "\\\\mathbb{N}"
}
});
} catch (e) {
console.error("KaTeX rendering error:", e);
}
}
// Function to fix and prepare text for LaTeX rendering
function prepareTextForLatex(text) {
if (!text) return text;
// Don't modify code blocks
if (text.indexOf('<pre>') !== -1) {
const parts = text.split(/<pre>|<\/pre>/);
for (let i = 0; i < parts.length; i++) {
// Only process odd-indexed parts (non-code)
if (i % 2 === 0) {
parts[i] = prepareLatexInText(parts[i]);
}
}
return parts.join('');
}
return prepareLatexInText(text);
}
// Helper to process LaTeX in regular text
function prepareLatexInText(text) {
// Make sure dollar signs used for math have proper spacing
// First, protect existing well-formed math expressions
text = text.replace(/(\\$\\$[^\\$]+\\$\\$)/g, '<protect>$1</protect>'); // protect display math
text = text.replace(/(\\$[^\\$\\n]+\\$)/g, '<protect>$1</protect>'); // protect inline math
// Fix common LaTeX formatting issues outside protected regions
text = text.replace(/([^<]protect[^>]*)(\\$)([^\\s])/g, '$1$2 $3'); // Add space after $ if needed
text = text.replace(/([^\\s])(\\$)([^<]protect[^>]*)/g, '$1 $2$3'); // Add space before $ if needed
// Handle subscripts: transform x_1 into x_{1} for better LaTeX compatibility
text = text.replace(/([a-zA-Z])_([0-9a-zA-Z])/g, '$1_{$2}');
// Restore protected content
text = text.replace(/<protect>(.*?)<\/protect>/g, '$1');
return text;
}
// Enhanced message processor for KaTeX rendering
function processMessage(message) {
if (!message) return;
try {
// Get direct textual content when possible
const elements = message.querySelectorAll('p, li, h1, h2, h3, h4, h5, span');
elements.forEach(el => {
const originalText = el.innerHTML;
const preparedText = prepareTextForLatex(originalText);
// Only update if changes were made
if (preparedText !== originalText) {
el.innerHTML = preparedText;
}
// Render equations in this element
renderMathInElement(el);
});
// Also try to render on the entire message as fallback
renderMathInElement(message);
} catch (e) {
console.error("Error processing message for LaTeX:", e);
}
}
// Function to monitor for new messages
function setupMathObserver() {
const chatElement = document.getElementById('chatbot');
if (!chatElement) {
setTimeout(setupMathObserver, 500);
return;
}
// Process any existing messages
chatElement.querySelectorAll('.message').forEach(processMessage);
// Set up observer for new content
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
if (mutation.addedNodes.length > 0 || mutation.type === 'characterData') {
chatElement.querySelectorAll('.message').forEach(processMessage);
break;
}
}
});
observer.observe(chatElement, {
childList: true,
subtree: true,
characterData: true
});
console.log("LaTeX rendering observer set up successfully");
}
// Initialize once the document is fully loaded
function initializeRendering() {
if (window.renderMathInElement) {
setupMathObserver();
} else {
// If KaTeX isn't loaded yet, wait for it
const katexScript = document.querySelector('script[src*="auto-render.min.js"]');
if (katexScript) {
katexScript.onload = setupMathObserver;
} else {
// Last resort: try again later
setTimeout(initializeRendering, 500);
}
}
}
// Set up multiple trigger points to ensure it loads
document.addEventListener('DOMContentLoaded', function() {
setTimeout(initializeRendering, 800);
});
window.addEventListener('load', function() {
setTimeout(initializeRendering, 1200);
});
</script>
"""

19
utils/table_utils.py Normal file
View File

@@ -0,0 +1,19 @@
from translations.lang_mappings import UI_TRANSLATIONS
def display_tables(current_tables, language=None):
"""Version simplifiée qui ignore le paramètre language"""
if not current_tables:
return None
html = ""
for table in current_tables:
table_data = table.get('data', '')
html += f"""
<div style="margin-bottom: 20px; border: 1px solid #ddd; padding: 15px; border-radius: 8px;">
<h3>{table.get('caption', 'Tableau')}</h3>
<p style="color:#666; font-size:0.9em;">Source: {table.get('source', 'N/A')}, Page: {table.get('page', 'N/A')}</p>
<pre>{table_data}</pre>
</div>
"""
return html if html else None