Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44bf0b868e | ||
|
|
c779c51a03 |
No files matched your search
+54
-2
@@ -3,7 +3,7 @@ from fastapi.responses import RedirectResponse, FileResponse
|
|||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
from typing import List
|
from typing import List
|
||||||
import os, json, asyncio, shutil
|
import os, json, asyncio, shutil, base64, io
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import pytz
|
import pytz
|
||||||
|
|
||||||
@@ -147,7 +147,59 @@ async def create_message(project_id: int, message: MessageCreate, db: Session =
|
|||||||
if message.files:
|
if message.files:
|
||||||
files_text = ""
|
files_text = ""
|
||||||
for f in message.files:
|
for f in message.files:
|
||||||
files_text += f"\n\n[Fichier attaché : {f.name}]\n```\n{f.content}\n```"
|
content = f.content
|
||||||
|
if content.startswith("data:"):
|
||||||
|
try:
|
||||||
|
header, b64data = content.split(",", 1)
|
||||||
|
file_bytes = base64.b64decode(b64data)
|
||||||
|
ext = f.name.split('.')[-1].lower()
|
||||||
|
extracted_text = ""
|
||||||
|
|
||||||
|
if ext == 'pdf':
|
||||||
|
try:
|
||||||
|
import PyPDF2
|
||||||
|
reader = PyPDF2.PdfReader(io.BytesIO(file_bytes))
|
||||||
|
extracted_text = "\n".join([page.extract_text() for page in reader.pages if page.extract_text()])
|
||||||
|
except ImportError:
|
||||||
|
extracted_text = "[Erreur: L'administrateur doit exécuter 'pip install PyPDF2' sur le serveur pour lire les PDF.]"
|
||||||
|
|
||||||
|
elif ext in ['xls', 'xlsx', 'xlsm', 'xlsb']:
|
||||||
|
try:
|
||||||
|
import openpyxl
|
||||||
|
wb = openpyxl.load_workbook(io.BytesIO(file_bytes), data_only=False)
|
||||||
|
for sheet_name in wb.sheetnames:
|
||||||
|
sheet = wb[sheet_name]
|
||||||
|
extracted_text += f"\n--- Feuille : {sheet_name} ---\n"
|
||||||
|
for row in sheet.iter_rows(values_only=True):
|
||||||
|
row_vals = [str(cell) if cell is not None else "" for cell in row]
|
||||||
|
if any(row_vals):
|
||||||
|
extracted_text += "\t".join(row_vals) + "\n"
|
||||||
|
|
||||||
|
# Extraction des Macros VBA
|
||||||
|
if ext in ['xlsm', 'xlsb', 'xls']:
|
||||||
|
try:
|
||||||
|
from oletools.olevba import VBA_Parser
|
||||||
|
vbaparser = VBA_Parser("filename", data=file_bytes)
|
||||||
|
if vbaparser.detect_vba_macros():
|
||||||
|
extracted_text += "\n\n--- MACROS VBA DETECTEES ---\n"
|
||||||
|
for (filename, stream_path, vba_filename, vba_code) in vbaparser.extract_macros():
|
||||||
|
extracted_text += f"\n// Module: {vba_filename}\n{vba_code}\n"
|
||||||
|
except ImportError:
|
||||||
|
extracted_text += "\n[Extraction VBA impossible: L'administrateur doit exécuter 'pip install oletools' sur le serveur.]\n"
|
||||||
|
except Exception as e:
|
||||||
|
extracted_text += f"\n[Erreur de lecture VBA interne: {str(e)}]\n"
|
||||||
|
|
||||||
|
except ImportError:
|
||||||
|
extracted_text = "[Erreur: L'administrateur doit exécuter 'pip install openpyxl' sur le serveur pour lire Excel.]"
|
||||||
|
else:
|
||||||
|
extracted_text = f"[Fichier binaire non supporté textuellement : {f.name}]"
|
||||||
|
|
||||||
|
files_text += f"\n\n[Fichier attaché : {f.name}]\n```text\n{extracted_text}\n```"
|
||||||
|
except Exception as e:
|
||||||
|
files_text += f"\n\n[Fichier attaché : {f.name} - ERREUR DE DECODAGE: {str(e)}]"
|
||||||
|
else:
|
||||||
|
files_text += f"\n\n[Fichier attaché : {f.name}]\n```\n{content}\n```"
|
||||||
|
|
||||||
final_content = message.content + files_text
|
final_content = message.content + files_text
|
||||||
|
|
||||||
db.add(Message(role=message.role, content=final_content, project_id=project_id))
|
db.add(Message(role=message.role, content=final_content, project_id=project_id))
|
||||||
|
|||||||
@@ -16,10 +16,9 @@ def determine_domain(model_id: str) -> str:
|
|||||||
return "Texte Polyvalent"
|
return "Texte Polyvalent"
|
||||||
|
|
||||||
async def translate_en_to_fr(client: httpx.AsyncClient, text: str) -> str:
|
async def translate_en_to_fr(client: httpx.AsyncClient, text: str) -> str:
|
||||||
"""Traduit automatiquement l'anglais vers le français via l'API publique Google Translate."""
|
|
||||||
if not text: return "Aucune description fournie."
|
if not text: return "Aucune description fournie."
|
||||||
try:
|
try:
|
||||||
short_text = text[:300].strip() # Limite stricte pour l'API gratuite
|
short_text = text[:300].strip()
|
||||||
url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=fr&dt=t&q={urllib.parse.quote(short_text)}"
|
url = f"https://translate.googleapis.com/translate_a/single?client=gtx&sl=en&tl=fr&dt=t&q={urllib.parse.quote(short_text)}"
|
||||||
resp = await client.get(url, timeout=4.0)
|
resp = await client.get(url, timeout=4.0)
|
||||||
if resp.status_code == 200:
|
if resp.status_code == 200:
|
||||||
@@ -205,7 +204,10 @@ async def run_orchestrator(db: Session, history: list, settings: SystemSettings,
|
|||||||
|
|
||||||
final_response = ""
|
final_response = ""
|
||||||
try:
|
try:
|
||||||
p_mod = config.get("prompter", "gemini-3.5-flash-lite")
|
# Fallback sécurisé en cas de champ vide
|
||||||
|
p_mod = config.get("prompter")
|
||||||
|
if not p_mod: p_mod = "gemini-3.5-flash-lite"
|
||||||
|
|
||||||
p_client, p_prov = get_client_for_model(db, p_mod, settings)
|
p_client, p_prov = get_client_for_model(db, p_mod, settings)
|
||||||
prompt_system = "You are an expert prompt engineer. Translate and optimize the user request into clear, precise English tailored for AI execution."
|
prompt_system = "You are an expert prompt engineer. Translate and optimize the user request into clear, precise English tailored for AI execution."
|
||||||
optimized = await ask_agent(p_client, p_mod, [{"role": "system", "content": prompt_system}, {"role": "user", "content": user_prompt}], p_prov)
|
optimized = await ask_agent(p_client, p_mod, [{"role": "system", "content": prompt_system}, {"role": "user", "content": user_prompt}], p_prov)
|
||||||
@@ -222,7 +224,9 @@ async def run_orchestrator(db: Session, history: list, settings: SystemSettings,
|
|||||||
w_tasks.append(ask_agent(w_client, w, formatted_history + [{"role": "user", "content": optimized}], w_prov))
|
w_tasks.append(ask_agent(w_client, w, formatted_history + [{"role": "user", "content": optimized}], w_prov))
|
||||||
responses = await asyncio.gather(*w_tasks, return_exceptions=True)
|
responses = await asyncio.gather(*w_tasks, return_exceptions=True)
|
||||||
|
|
||||||
c_mod = config.get("concatenator", "gemini-3.5-flash-lite")
|
c_mod = config.get("concatenator")
|
||||||
|
if not c_mod: c_mod = "gemini-3.5-flash-lite"
|
||||||
|
|
||||||
c_client, c_prov = get_client_for_model(db, c_mod, settings)
|
c_client, c_prov = get_client_for_model(db, c_mod, settings)
|
||||||
|
|
||||||
concat_system = (
|
concat_system = (
|
||||||
@@ -233,7 +237,7 @@ async def run_orchestrator(db: Session, history: list, settings: SystemSettings,
|
|||||||
"You may translate code comments into French if appropriate, but leave code syntax strictly intact."
|
"You may translate code comments into French if appropriate, but leave code syntax strictly intact."
|
||||||
)
|
)
|
||||||
|
|
||||||
synth = f"User Request: {user_prompt}\n\n" + "\n".join([f"--- EXPERT {i+1} ---\n{r}" for i, r in enumerate(responses)])
|
synth = f"User Request: {user_prompt}\n\n" + "\n".join([f"--- EXPERT {i+1} ---\n{str(r)}" for i, r in enumerate(responses)])
|
||||||
final_response = await ask_agent(c_client, c_mod, [{"role": "system", "content": concat_system}, {"role": "user", "content": synth}], c_prov)
|
final_response = await ask_agent(c_client, c_mod, [{"role": "system", "content": concat_system}, {"role": "user", "content": synth}], c_prov)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
+49
-33
@@ -293,7 +293,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-4 md:px-6 md:pt-6 pb-2 w-full max-w-7xl mx-auto flex-shrink-0">
|
<div class="p-4 md:px-6 md:pt-6 pb-2 w-full max-w-7xl mx-auto flex-shrink-0">
|
||||||
<!-- EN-TÊTE CONFIGURATION IA (AVEC BOUTONS IMPORT/EXPORT) -->
|
|
||||||
<div class="flex justify-between items-center mb-2">
|
<div class="flex justify-between items-center mb-2">
|
||||||
<span class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Configuration des IA</span>
|
<span class="text-xs font-semibold text-gray-400 uppercase tracking-wider">Configuration des IA</span>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
@@ -305,10 +304,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- BARRE DE SÉLECTION DE STYLE TABULAIRE (9 COLONNES STRICTES) -->
|
<!-- BARRE DE SÉLECTION -->
|
||||||
<div class="grid grid-cols-9 border border-black bg-gray-900 rounded-lg overflow-hidden text-center text-xs font-bold shadow-lg mb-4">
|
<div class="grid grid-cols-9 border border-black bg-gray-900 rounded-lg overflow-hidden text-center text-xs font-bold shadow-lg mb-4">
|
||||||
|
|
||||||
<!-- 1. PAYANT / GRATUIT -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-gray-400 mb-1">PRIX</label>
|
<label class="block text-[9px] text-gray-400 mb-1">PRIX</label>
|
||||||
<select v-model="colPricing" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none">
|
<select v-model="colPricing" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none">
|
||||||
@@ -318,7 +315,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. TYPE (DOMAINE) -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-gray-400 mb-1">TYPE</label>
|
<label class="block text-[9px] text-gray-400 mb-1">TYPE</label>
|
||||||
<select v-model="colDomain" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none">
|
<select v-model="colDomain" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none">
|
||||||
@@ -330,7 +326,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 3. TRAVAILLEUR 1 -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 1</label>
|
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 1</label>
|
||||||
<select v-model="workers[0]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="workers[0]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
@@ -339,7 +334,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 4. TRAVAILLEUR 2 -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 2</label>
|
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 2</label>
|
||||||
<select v-model="workers[1]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="workers[1]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
@@ -348,7 +342,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 5. TRAVAILLEUR 3 -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 3</label>
|
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 3</label>
|
||||||
<select v-model="workers[2]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="workers[2]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
@@ -357,7 +350,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 6. TRAVAILLEUR 4 -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 4</label>
|
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 4</label>
|
||||||
<select v-model="workers[3]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="workers[3]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
@@ -366,7 +358,6 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 7. TRAVAILLEUR 5 -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 5</label>
|
<label class="block text-[9px] text-aethas-cyan mb-1">TRAVAILLEUR 5</label>
|
||||||
<select v-model="workers[4]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="workers[4]" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
@@ -375,29 +366,25 @@
|
|||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 8. PROMPTEUR -->
|
|
||||||
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="border-r border-black p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-gray-400 mb-1">PROMPTEUR</label>
|
<label class="block text-[9px] text-gray-400 mb-1">PROMPTEUR</label>
|
||||||
<select v-model="selectedPrompter" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="selectedPrompter" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
<option value="gemini-3.5-flash-lite">Gemini Flash</option>
|
<option value="">(Par défaut)</option>
|
||||||
<option v-for="m in getFilteredModelsForCol(colPricing, colDomain, selectedPrompter)" :key="m.model_id" :value="m.model_id">{{ m.name || m.model_id }}</option>
|
<option v-for="m in getFilteredModelsForCol(colPricing, colDomain, selectedPrompter)" :key="m.model_id" :value="m.model_id">{{ m.name || m.model_id }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 9. CONCATENEUR -->
|
|
||||||
<div class="p-1.5 bg-gray-800 flex flex-col justify-center">
|
<div class="p-1.5 bg-gray-800 flex flex-col justify-center">
|
||||||
<label class="block text-[9px] text-aethas-purple mb-1">CONCATENEUR</label>
|
<label class="block text-[9px] text-aethas-purple mb-1">CONCATENEUR</label>
|
||||||
<select v-model="selectedConcatenator" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
<select v-model="selectedConcatenator" class="w-full bg-aethas-dark border border-gray-700 rounded text-gray-200 text-[10px] p-1 outline-none truncate">
|
||||||
<option value="gemini-3.5-flash-lite">Gemini Flash</option>
|
<option value="">(Par défaut)</option>
|
||||||
<option v-for="m in getFilteredModelsForCol(colPricing, colDomain, selectedConcatenator)" :key="m.model_id" :value="m.model_id">{{ m.name || m.model_id }}</option>
|
<option v-for="m in getFilteredModelsForCol(colPricing, colDomain, selectedConcatenator)" :key="m.model_id" :value="m.model_id">{{ m.name || m.model_id }}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Champ de texte et boutons d'envoi / multi-fichiers -->
|
<!-- CHAT INPUT -->
|
||||||
<div class="relative flex items-end bg-gray-800 border border-gray-600 rounded-2xl p-2 shadow-lg focus-within:border-aethas-cyan flex-col gap-2">
|
<div class="relative flex items-end bg-gray-800 border border-gray-600 rounded-2xl p-2 shadow-lg focus-within:border-aethas-cyan flex-col gap-2">
|
||||||
|
|
||||||
<!-- Liste des fichiers attachés -->
|
|
||||||
<div v-if="attachedFiles.length > 0" class="w-full flex flex-wrap gap-2 px-2 pt-2">
|
<div v-if="attachedFiles.length > 0" class="w-full flex flex-wrap gap-2 px-2 pt-2">
|
||||||
<div v-for="(file, index) in attachedFiles" :key="index" class="bg-gray-900 border border-gray-700 rounded px-2 py-1 text-xs text-gray-300 flex items-center gap-2">
|
<div v-for="(file, index) in attachedFiles" :key="index" class="bg-gray-900 border border-gray-700 rounded px-2 py-1 text-xs text-gray-300 flex items-center gap-2">
|
||||||
<i data-lucide="file" class="w-3.5 h-3.5 text-aethas-cyan"></i>
|
<i data-lucide="file" class="w-3.5 h-3.5 text-aethas-cyan"></i>
|
||||||
@@ -457,9 +444,9 @@
|
|||||||
const colPricing = ref("");
|
const colPricing = ref("");
|
||||||
const colDomain = ref("");
|
const colDomain = ref("");
|
||||||
|
|
||||||
const workers = ref(["gemini-3.5-flash-lite", "", "", "", ""]);
|
const workers = ref(["", "", "", "", ""]);
|
||||||
const selectedPrompter = ref("gemini-3.5-flash-lite");
|
const selectedPrompter = ref("");
|
||||||
const selectedConcatenator = ref("gemini-3.5-flash-lite");
|
const selectedConcatenator = ref("");
|
||||||
|
|
||||||
const attachedFiles = ref([]);
|
const attachedFiles = ref([]);
|
||||||
|
|
||||||
@@ -478,14 +465,26 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
const getFilteredModelsForCol = (pricingFilter, domainFilter, currentSelectedId) => {
|
const getFilteredModelsForCol = (pricingFilter, domainFilter, currentSelectedId) => {
|
||||||
return modelInfo.models.filter(m => {
|
let filtered = modelInfo.models.filter(m => {
|
||||||
if (m.model_id === currentSelectedId) return true;
|
|
||||||
const matchDomain = domainFilter === "" || m.domain === domainFilter;
|
const matchDomain = domainFilter === "" || m.domain === domainFilter;
|
||||||
let matchPrice = true;
|
let matchPrice = true;
|
||||||
if (pricingFilter === 'free') matchPrice = m.is_free;
|
if (pricingFilter === 'free') matchPrice = m.is_free;
|
||||||
if (pricingFilter === 'paid') matchPrice = !m.is_free;
|
if (pricingFilter === 'paid') matchPrice = !m.is_free;
|
||||||
return matchDomain && matchPrice;
|
return matchDomain && matchPrice;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (currentSelectedId && currentSelectedId.trim() !== "") {
|
||||||
|
const exists = filtered.find(m => m.model_id === currentSelectedId);
|
||||||
|
if (!exists) {
|
||||||
|
const original = modelInfo.models.find(m => m.model_id === currentSelectedId);
|
||||||
|
if (original) {
|
||||||
|
filtered.unshift(original);
|
||||||
|
} else {
|
||||||
|
filtered.unshift({ model_id: currentSelectedId, name: currentSelectedId, domain: "Importé", pricing_prompt: 0, pricing_completion: 0 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return filtered;
|
||||||
};
|
};
|
||||||
|
|
||||||
const formatDate = (isoString) => {
|
const formatDate = (isoString) => {
|
||||||
@@ -546,7 +545,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
const downloadTemplate = () => {
|
const downloadTemplate = () => {
|
||||||
const templateContent = `# AETHAS38 - Configuration Multi-Agents\n\n**Rappel important :**\n- **Prompteur :** Le système de l'orchestrateur utilisera ce modèle pour traduire et optimiser votre requête initiale en ANGLAIS (langue native des IA).\n- **Concaténeur :** Le système utilisera ce modèle pour regrouper les réponses des travailleurs et traduire la synthèse finale en FRANÇAIS (en préservant le code brut).\n\n## Sélection des Modèles\nRenseignez l'ID exact du modèle (ex: gemini-3.5-flash-lite, mistralai/mistral-large-4-0). Laissez vide si inutilisé.\n\n- Prompteur : gemini-3.5-flash-lite\n- Travailleur 1 : \n- Travailleur 2 : \n- Travailleur 3 : \n- Travailleur 4 : \n- Travailleur 5 : \n- Concaténeur : gemini-3.5-flash-lite\n`;
|
const templateContent = `# AETHAS38 - Configuration Multi-Agents\n\n**Rappel important :**\n- **Prompteur :** Le système de l'orchestrateur utilisera ce modèle pour traduire et optimiser votre requête initiale en ANGLAIS (langue native des IA).\n- **Concaténeur :** Le système utilisera ce modèle pour regrouper les réponses des travailleurs et traduire la synthèse finale en FRANÇAIS (en préservant le code brut).\n\n## Sélection des Modèles\nRenseignez l'ID exact du modèle (ex: gemini-3.5-flash-lite, mistralai/mistral-large-4-0). Laissez vide si inutilisé.\n\n- Prompteur : \n- Travailleur 1 : \n- Travailleur 2 : \n- Travailleur 3 : \n- Travailleur 4 : \n- Travailleur 5 : \n- Concaténeur : \n`;
|
||||||
downloadFile(templateContent, 'aethas38-config-agents.md');
|
downloadFile(templateContent, 'aethas38-config-agents.md');
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -556,21 +555,23 @@
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
const content = e.target.result;
|
const content = e.target.result;
|
||||||
|
|
||||||
const extractModel = (key) => {
|
const extractModel = (key) => {
|
||||||
const regex = new RegExp(`- ${key} : (.*)`, 'i');
|
const safeKey = key.replace(/[éèe]/gi, '[éèe]');
|
||||||
|
const regex = new RegExp(`-\\s*${safeKey}\\s*:\\s*([^\\r\\n]*)`, 'i');
|
||||||
const match = content.match(regex);
|
const match = content.match(regex);
|
||||||
return match && match[1] ? match[1].trim() : "";
|
return match && match[1] ? match[1].trim() : "";
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkAndSet = (val, defaultVal) => val !== "" ? val : defaultVal;
|
const w1 = extractModel('Travailleur 1');
|
||||||
|
const w2 = extractModel('Travailleur 2');
|
||||||
|
const w3 = extractModel('Travailleur 3');
|
||||||
|
const w4 = extractModel('Travailleur 4');
|
||||||
|
const w5 = extractModel('Travailleur 5');
|
||||||
|
|
||||||
selectedPrompter.value = checkAndSet(extractModel('Prompteur'), 'gemini-3.5-flash-lite');
|
workers.value = [w1, w2, w3, w4, w5];
|
||||||
workers.value[0] = extractModel('Travailleur 1');
|
selectedPrompter.value = extractModel('Prompteur');
|
||||||
workers.value[1] = extractModel('Travailleur 2');
|
selectedConcatenator.value = extractModel('Concateneur');
|
||||||
workers.value[2] = extractModel('Travailleur 3');
|
|
||||||
workers.value[3] = extractModel('Travailleur 4');
|
|
||||||
workers.value[4] = extractModel('Travailleur 5');
|
|
||||||
selectedConcatenator.value = checkAndSet(extractModel('Concaténeur'), 'gemini-3.5-flash-lite');
|
|
||||||
|
|
||||||
alert("Configuration IA importée et appliquée avec succès !");
|
alert("Configuration IA importée et appliquée avec succès !");
|
||||||
};
|
};
|
||||||
@@ -623,10 +624,19 @@
|
|||||||
|
|
||||||
const processFile = (file) => {
|
const processFile = (file) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
const textExts = ['.txt', '.md', '.csv', '.json', '.py', '.js', '.html', '.css', '.xml', '.yml', '.yaml', '.sh', '.bat', '.ps1', '.ini', '.cfg', '.conf', '.log'];
|
||||||
|
const isText = textExts.some(ext => file.name.toLowerCase().endsWith(ext)) || file.type.startsWith('text/');
|
||||||
|
|
||||||
reader.onload = (e) => {
|
reader.onload = (e) => {
|
||||||
attachedFiles.value.push({ name: file.name, content: e.target.result });
|
attachedFiles.value.push({ name: file.name, content: e.target.result });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Les fichiers binaires (PDF, Excel, etc.) sont lus en Base64 pour que Python s'en charge.
|
||||||
|
if (isText) {
|
||||||
reader.readAsText(file);
|
reader.readAsText(file);
|
||||||
|
} else {
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeFile = (index) => { attachedFiles.value.splice(index, 1); };
|
const removeFile = (index) => { attachedFiles.value.splice(index, 1); };
|
||||||
@@ -680,7 +690,13 @@
|
|||||||
} else {
|
} else {
|
||||||
const err = await response.json(); alert("Erreur API IA : " + err.detail);
|
const err = await response.json(); alert("Erreur API IA : " + err.detail);
|
||||||
}
|
}
|
||||||
} catch (error) {} finally { isAiThinking.value = false; scrollToBottom(); nextTick(()=>lucide.createIcons()); }
|
} catch (error) {
|
||||||
|
alert("Erreur locale lors de l'envoi : " + error.message);
|
||||||
|
} finally {
|
||||||
|
isAiThinking.value = false;
|
||||||
|
scrollToBottom();
|
||||||
|
nextTick(()=>lucide.createIcons());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const changePassword = async () => {
|
const changePassword = async () => {
|
||||||
|
|||||||
@@ -15,3 +15,6 @@ openai
|
|||||||
httpx
|
httpx
|
||||||
python-multipart
|
python-multipart
|
||||||
pytz
|
pytz
|
||||||
|
PyPDF2
|
||||||
|
openpyxl
|
||||||
|
oletools
|
||||||
Reference in new issue
Block a user