data-primals-engine 1.6.3 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +52 -900
- package/client/index.js +3 -0
- package/client/package-lock.json +7563 -8811
- package/client/package.json +11 -1
- package/client/src/App.scss +29 -0
- package/client/src/AssistantChat.jsx +369 -362
- package/client/src/DataEditor.jsx +383 -383
- package/client/src/DataLayout.jsx +54 -20
- package/client/src/ModelList.jsx +280 -280
- package/client/src/ViewSwitcher.scss +0 -31
- package/client/src/_variables.scss +3 -0
- package/client/src/constants.js +81 -100
- package/client/src/contexts/CommandContext.jsx +274 -259
- package/client/vite.config.js +30 -30
- package/doc/AI-assistance.md +93 -0
- package/doc/Advanced-workflows.md +90 -0
- package/doc/Event-system.md +79 -0
- package/doc/Packs-gallery.md +73 -0
- package/doc/automation-workflows.md +102 -0
- package/doc/core-concepts.md +33 -0
- package/doc/custom-api-endpoints.md +40 -0
- package/doc/dashboards-kpis-charts.md +49 -0
- package/doc/data-management.md +120 -0
- package/doc/data-models.md +75 -0
- package/doc/index.md +14 -0
- package/doc/roles-permissions.md +43 -0
- package/doc/users.md +30 -0
- package/package.json +20 -10
- package/src/client.js +6 -4
- package/src/constants.js +1 -1
- package/src/core.js +31 -12
- package/src/defaultModels.js +1 -1
- package/src/engine.js +342 -335
- package/src/filter.js +72 -173
- package/src/migrate.js +1 -1
- package/src/modules/assistant/assistant.js +30 -20
- package/src/modules/data/data.backup.js +4 -4
- package/src/modules/data/data.js +8 -7
- package/src/modules/data/data.operations.js +186 -133
- package/src/modules/data/data.relations.js +3 -2
- package/src/modules/data/data.scheduling.js +1 -1
- package/src/modules/mongodb.js +17 -8
- package/src/modules/swagger.js +25 -5
- package/src/modules/user.js +108 -79
- package/src/modules/workflow.js +138 -3
- package/src/packs.js +5697 -5697
- package/src/profiles.js +19 -0
- package/swagger-en.yml +3391 -1550
- package/swagger-fr.yml +3385 -2896
- package/test/assistant.test.js +2 -2
- package/test/core.test.js +341 -0
- package/test/data.backup.integration.test.js +3 -1
- package/test/data.history.integration.test.js +0 -1
- package/test/data.integration.test.js +137 -2
- package/test/user.test.js +33 -29
package/src/filter.js
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import {getCollectionForUser, isObjectId} from "./modules/mongodb.js";
|
|
2
|
-
import {getModel} from "./modules/data/index.js";
|
|
3
|
-
import {ObjectId} from "mongodb";
|
|
4
|
-
import {safeAssignObject} from "./core.js";
|
|
5
1
|
|
|
6
2
|
let safeRegex = null; // Par défaut, aucune fonction de validation n'est définie.
|
|
7
3
|
|
|
@@ -13,6 +9,47 @@ export function setSafeRegex(validator) {
|
|
|
13
9
|
safeRegex = validator;
|
|
14
10
|
}
|
|
15
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Évalue une comparaison entre une valeur cible et une valeur de condition.
|
|
14
|
+
* @param {string} operator - L'opérateur de comparaison (ex: '$eq', '$lt').
|
|
15
|
+
* @param {*} targetValue - La valeur actuelle du champ dans les données.
|
|
16
|
+
* @param {*} processedConditionValue - La valeur de la condition à comparer.
|
|
17
|
+
* @param {object} condition - La condition complète pour le contexte de logging.
|
|
18
|
+
* @returns {boolean} - Le résultat de la comparaison.
|
|
19
|
+
*/
|
|
20
|
+
function evaluateComparison(operator, targetValue, processedConditionValue, condition) {
|
|
21
|
+
const logClientEvalWarning = (message, details) => {
|
|
22
|
+
console.warn(`[Client Eval] ${message}:`, details);
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
switch (operator) {
|
|
27
|
+
case '$eq': return targetValue == processedConditionValue; // Utilise '==' pour une comparaison plus souple
|
|
28
|
+
case '$ne': return targetValue != processedConditionValue;
|
|
29
|
+
case '$gt': return targetValue > processedConditionValue;
|
|
30
|
+
case '$lt': return targetValue < processedConditionValue;
|
|
31
|
+
case '$gte': return targetValue >= processedConditionValue;
|
|
32
|
+
case '$lte': return targetValue <= processedConditionValue;
|
|
33
|
+
case '$regex':
|
|
34
|
+
if (typeof targetValue !== 'string' || !processedConditionValue || typeof processedConditionValue !== 'string') return false;
|
|
35
|
+
try {
|
|
36
|
+
if (safeRegex && !safeRegex(processedConditionValue)) return false;
|
|
37
|
+
const regex = new RegExp(processedConditionValue, 'i');
|
|
38
|
+
return regex.test(targetValue);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
logClientEvalWarning(`Invalid regex pattern: ${processedConditionValue}`, condition);
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
case '$in': return Array.isArray(processedConditionValue) && processedConditionValue.includes(targetValue);
|
|
44
|
+
case '$nin': return !Array.isArray(processedConditionValue) || !processedConditionValue.includes(targetValue);
|
|
45
|
+
default: return true; // Permissif par défaut pour les opérateurs non gérés
|
|
46
|
+
}
|
|
47
|
+
} catch (evalError) {
|
|
48
|
+
logClientEvalWarning(`Error during condition evaluation: ${operator}, targetValue=${targetValue}, processedConditionValue=${processedConditionValue}`, condition);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
16
53
|
/**
|
|
17
54
|
* Récupère une valeur imbriquée dans un objet en utilisant une chaîne de chemin.
|
|
18
55
|
* Gère les tableaux et les objets. Retourne undefined si le chemin n'est pas trouvé.
|
|
@@ -67,6 +104,17 @@ const evaluateSingleCondition = (currentModelDef, condition, formData, allModels
|
|
|
67
104
|
return true; // Permissive default
|
|
68
105
|
}
|
|
69
106
|
|
|
107
|
+
// If the condition is a standard query operator like { amount: { $lt: 1000 } }
|
|
108
|
+
const fieldNameFromCondition = Object.keys(condition)[0];
|
|
109
|
+
const conditionValueObject = condition[fieldNameFromCondition];
|
|
110
|
+
|
|
111
|
+
if (typeof conditionValueObject === 'object' && conditionValueObject !== null && !Array.isArray(conditionValueObject)) {
|
|
112
|
+
const operator = Object.keys(conditionValueObject)[0];
|
|
113
|
+
if (operator.startsWith('$')) {
|
|
114
|
+
return evaluateComparison(operator, formData[fieldNameFromCondition], conditionValueObject[operator], condition, checkRegex);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
70
118
|
// Si la condition est de la forme {field: value}, on la transforme en {$eq: value}
|
|
71
119
|
if (!Object.keys(condition)[0].startsWith('$') && typeof condition[Object.keys(condition)[0]] !== 'object') {
|
|
72
120
|
const fieldName = Object.keys(condition)[0];
|
|
@@ -81,12 +129,12 @@ const evaluateSingleCondition = (currentModelDef, condition, formData, allModels
|
|
|
81
129
|
const [fieldPath, expectedValue] = condition[operator];
|
|
82
130
|
if (typeof fieldPath === 'string' && fieldPath.startsWith('$')) {
|
|
83
131
|
const fieldName = fieldPath.substring(1); // Enlève le $ devant
|
|
84
|
-
const actualValue = formData
|
|
85
|
-
|
|
86
|
-
if (operator === '$ne') return actualValue != expectedValue;
|
|
132
|
+
const actualValue = getNestedValue(formData, fieldName);
|
|
133
|
+
return evaluateComparison(operator, actualValue, expectedValue, condition, checkRegex);
|
|
87
134
|
}
|
|
88
135
|
}
|
|
89
136
|
|
|
137
|
+
|
|
90
138
|
// Si la condition contient des opérateurs logiques, on les gère ici
|
|
91
139
|
if (condition.$and || condition.$or || condition.$not || condition.$nor) {
|
|
92
140
|
console.warn("[Client Eval] Condition logique détectée dans evaluateSingleCondition, ce n'est pas attendu. Devrait être géré par isConditionMet.");
|
|
@@ -165,7 +213,7 @@ const evaluateSingleCondition = (currentModelDef, condition, formData, allModels
|
|
|
165
213
|
return false;
|
|
166
214
|
}
|
|
167
215
|
|
|
168
|
-
return evaluateComparison(
|
|
216
|
+
return evaluateComparison('$eq', targetValue, processedConditionValue, condition);
|
|
169
217
|
|
|
170
218
|
function logClientEvalWarning(message, details) {
|
|
171
219
|
console.warn(`[Client Eval] ${message}:`, details);
|
|
@@ -189,43 +237,6 @@ const evaluateSingleCondition = (currentModelDef, condition, formData, allModels
|
|
|
189
237
|
}
|
|
190
238
|
}
|
|
191
239
|
|
|
192
|
-
function evaluateComparison(operator, targetValue, processedConditionValue, condition) {
|
|
193
|
-
try {
|
|
194
|
-
switch (typeof operator === 'object' ? Object.keys(operator)[0] : null) {
|
|
195
|
-
case '$eq': return targetValue === processedConditionValue;
|
|
196
|
-
case '$ne': return targetValue !== processedConditionValue;
|
|
197
|
-
case '$gt': return targetValue > processedConditionValue;
|
|
198
|
-
case '$lt': return targetValue < processedConditionValue;
|
|
199
|
-
case '$gte': return targetValue >= processedConditionValue;
|
|
200
|
-
case '$lte': return targetValue <= processedConditionValue;
|
|
201
|
-
case '$regex':
|
|
202
|
-
if (typeof targetValue !== 'string') return false;
|
|
203
|
-
if (typeof processedConditionValue !== 'string') return false;
|
|
204
|
-
try {
|
|
205
|
-
// On vérifie si la fonction a été injectée ET si la regex est sûre.
|
|
206
|
-
// Côté client, `safeRegex` sera null, donc la vérification est simplement ignorée.
|
|
207
|
-
if( checkRegex && safeRegex && safeRegex(processedConditionValue)) {
|
|
208
|
-
const regex = new RegExp(processedConditionValue, 'i');
|
|
209
|
-
return regex.test(targetValue);
|
|
210
|
-
}
|
|
211
|
-
return false;
|
|
212
|
-
} catch (e) {
|
|
213
|
-
logClientEvalWarning(`Invalid regex pattern: ${processedConditionValue}`, condition);
|
|
214
|
-
return false;
|
|
215
|
-
}
|
|
216
|
-
case '$in':
|
|
217
|
-
return Array.isArray(processedConditionValue) && processedConditionValue.includes(String(targetValue));
|
|
218
|
-
case '$nin':
|
|
219
|
-
return !Array.isArray(processedConditionValue) || !processedConditionValue.includes(String(targetValue));
|
|
220
|
-
default:
|
|
221
|
-
logClientEvalWarning(`Unhandled operator in client evaluation logic: ${operator}`, condition);
|
|
222
|
-
return true; // Permissive default
|
|
223
|
-
}
|
|
224
|
-
} catch (evalError) {
|
|
225
|
-
logClientEvalWarning(`Error during client condition evaluation: ${operator}, targetValue=${targetValue}, processedConditionValue=${processedConditionValue}`, condition);
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
240
|
};
|
|
230
241
|
|
|
231
242
|
export const isConditionMet = (model, cond, formData, allModels, user,checkRegex=true) => {
|
|
@@ -237,6 +248,22 @@ export const isConditionMet = (model, cond, formData, allModels, user,checkRegex
|
|
|
237
248
|
return true;
|
|
238
249
|
}
|
|
239
250
|
|
|
251
|
+
// --- NOUVELLE LOGIQUE ---
|
|
252
|
+
// Gère les conditions de type expression d'agrégation comme { "$lt": ["$amount", 1000] }
|
|
253
|
+
const operatorKeys = Object.keys(condition).filter(k => k.startsWith('$'));
|
|
254
|
+
if (operatorKeys.length === 1) {
|
|
255
|
+
const operator = operatorKeys[0];
|
|
256
|
+
const operands = condition[operator];
|
|
257
|
+
|
|
258
|
+
if (Array.isArray(operands) && operands.length === 2 && typeof operands[0] === 'string' && operands[0].startsWith('$')) {
|
|
259
|
+
const fieldName = operands[0].substring(1); // Extrait 'amount' de '$amount'
|
|
260
|
+
const expectedValue = operands[1];
|
|
261
|
+
const actualValue = getNestedValue(formData, fieldName);
|
|
262
|
+
|
|
263
|
+
return evaluateComparison(operator, actualValue, expectedValue, condition, checkRegex);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
240
267
|
// Cas spécial: Évaluation sans modèle (ex: pour les webhooks où la condition est déjà résolue)
|
|
241
268
|
// Dans ce mode, la condition doit être un objet avec un seul opérateur.
|
|
242
269
|
if (!model) {
|
|
@@ -314,131 +341,3 @@ export const isConditionMet = (model, cond, formData, allModels, user,checkRegex
|
|
|
314
341
|
return evaluateSingleCondition(model, condition, formData, allModels, user, checkRegex);
|
|
315
342
|
};
|
|
316
343
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Remplace les placeholders dans un template (string, object, array) par des valeurs du contextData.
|
|
321
|
-
* Version améliorée avec support des chemins complexes via resolvePathValue.
|
|
322
|
-
*/
|
|
323
|
-
export async function substituteVariables(template, contextData, user) {
|
|
324
|
-
// 1. Retourner les types non substituables tels quels
|
|
325
|
-
if (template === null || (typeof template !== 'string' && typeof template !== 'object')) {
|
|
326
|
-
return template;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// 2. Gérer les tableaux de manière récursive
|
|
330
|
-
if (Array.isArray(template)) {
|
|
331
|
-
return Promise.all(template.map(item => substituteVariables(item, contextData, user)));
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// 3. Gérer les objets de manière récursive
|
|
335
|
-
if (typeof template === 'object') {
|
|
336
|
-
const newObj = {};
|
|
337
|
-
for (const key in template) {
|
|
338
|
-
if (Object.prototype.hasOwnProperty.call(template, key)) {
|
|
339
|
-
const val = await substituteVariables(template[key], contextData, user);
|
|
340
|
-
safeAssignObject(newObj, key, val);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
return newObj;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// --- À partir d'ici, nous savons que `template` est une chaîne de caractères ---
|
|
347
|
-
|
|
348
|
-
// 4. Construire le contexte complet pour la substitution
|
|
349
|
-
const dbCollection = await getCollectionForUser(user);
|
|
350
|
-
const userEnvVars = await dbCollection.find({ _model: 'env', _user: user.username }).toArray();
|
|
351
|
-
const userEnv = userEnvVars.reduce((acc, v) => ({ ...acc, [v.name]: v.value }), {});
|
|
352
|
-
|
|
353
|
-
// `contextToSearch` contient toutes les données disponibles à sa racine
|
|
354
|
-
const contextToSearch = { ...contextData, env: userEnv };
|
|
355
|
-
|
|
356
|
-
// 5. Logique de résolution de valeur améliorée avec resolvePathValue
|
|
357
|
-
const findValue = async (key) => {
|
|
358
|
-
let path = key.trim();
|
|
359
|
-
if (path.startsWith('context.')) {
|
|
360
|
-
path = path.substring('context.'.length);
|
|
361
|
-
}
|
|
362
|
-
if (path.endsWith('._id')) {
|
|
363
|
-
const basePath = path.slice(0, -4);
|
|
364
|
-
const value = await findValue(basePath);
|
|
365
|
-
return value?._id?.toString(); // Convertit l'ObjectId en string
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// Gérer les valeurs dynamiques spéciales
|
|
369
|
-
if (path === 'now') {
|
|
370
|
-
return new Date().toISOString();
|
|
371
|
-
} else if (path === 'randomUUID') {
|
|
372
|
-
return crypto.randomUUID();
|
|
373
|
-
} else if( path === "baseUrl" ){
|
|
374
|
-
return process.env.NODE_ENV === 'production' ? 'https://'+getHost()+'/' : 'http://localhost:/'+port;
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
// Détecter si le chemin est complexe (contient plus d'un point)
|
|
378
|
-
if (path.split('.').length > 1) {
|
|
379
|
-
try {
|
|
380
|
-
// Essayer de résoudre le chemin avec resolvePathValue
|
|
381
|
-
const [root, ...rest] = path.split('.');
|
|
382
|
-
// On vérifie si la racine du chemin (ex: 'triggerData') existe dans notre contexte
|
|
383
|
-
if (contextToSearch[root]) {
|
|
384
|
-
const resolvedValue = await resolvePathValue(
|
|
385
|
-
rest.join('.'),
|
|
386
|
-
contextToSearch[root], // On passe le bon objet de départ (ex: l'objet triggerData)
|
|
387
|
-
user
|
|
388
|
-
);
|
|
389
|
-
if (resolvedValue !== undefined) {
|
|
390
|
-
return resolvedValue;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
} catch (error) {
|
|
394
|
-
console.warn(`Erreur lors de la résolution du chemin "${path}":`, error.message);
|
|
395
|
-
// On continue avec la méthode normale si la résolution échoue
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
// Fallback: chercher le chemin dans l'objet de contexte normal
|
|
400
|
-
return getNestedValue(contextToSearch, path);
|
|
401
|
-
};
|
|
402
|
-
|
|
403
|
-
// CAS A : La chaîne est un unique placeholder (ex: "{context.triggerData.product.price}")
|
|
404
|
-
const singlePlaceholderMatch = template.match(/^\{([^}]+)\}$/);
|
|
405
|
-
if (singlePlaceholderMatch) {
|
|
406
|
-
const key = singlePlaceholderMatch[1];
|
|
407
|
-
const value = await findValue(key);
|
|
408
|
-
|
|
409
|
-
if (value === undefined) {
|
|
410
|
-
return template; // Placeholder not found, return as is.
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
// If the resolved value is a string, it might contain more placeholders.
|
|
414
|
-
// We recursively call substituteVariables on it, but only if it's different
|
|
415
|
-
// from the original template to prevent infinite loops.
|
|
416
|
-
if (typeof value === 'string' && value !== template) {
|
|
417
|
-
return substituteVariables(value, contextData, user);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
// For non-string values or if value is same as template, return the value.
|
|
421
|
-
return value;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
// CAS B : La chaîne contient plusieurs placeholders ou mix texte/variables
|
|
425
|
-
const placeholderRegex = /\{([^}]+)\}/g;
|
|
426
|
-
const placeholders = [...template.matchAll(placeholderRegex)];
|
|
427
|
-
|
|
428
|
-
// Si aucun placeholder trouvé, retourner la chaîne telle quelle
|
|
429
|
-
if (placeholders.length === 0) {
|
|
430
|
-
return template;
|
|
431
|
-
}
|
|
432
|
-
|
|
433
|
-
// Remplacer chaque placeholder de manière asynchrone
|
|
434
|
-
let result = template;
|
|
435
|
-
for (const [match, key] of placeholders) {
|
|
436
|
-
const value = await findValue(key);
|
|
437
|
-
const replacement = value !== undefined
|
|
438
|
-
? (value === null ? 'null' : typeof value === 'object' ? JSON.stringify(value) : String(value))
|
|
439
|
-
: match;
|
|
440
|
-
result = result.replace(match, replacement);
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
return result;
|
|
444
|
-
}
|
package/src/migrate.js
CHANGED
|
@@ -24,7 +24,7 @@ engine.start(port, async () => {
|
|
|
24
24
|
const target = process.argv[3]; // Le nom du fichier de migration cible
|
|
25
25
|
|
|
26
26
|
try {
|
|
27
|
-
const db = MongoDatabase;
|
|
27
|
+
const db = MongoDatabase();
|
|
28
28
|
const { executedNames, allMigrationFiles } = await getMigrationStatus(db);
|
|
29
29
|
|
|
30
30
|
switch (command) {
|
|
@@ -8,7 +8,7 @@ import {Config} from "../../config.js";
|
|
|
8
8
|
import { Event } from "../../events.js";
|
|
9
9
|
import rateLimit from "express-rate-limit";
|
|
10
10
|
import {generateLimiter} from "../user.js";
|
|
11
|
-
import {
|
|
11
|
+
import {maxAIReflectiveSteps} from "../../constants.js";
|
|
12
12
|
import i18n from "../../i18n.js";
|
|
13
13
|
import {parseSafeJSON} from "../../core.js";
|
|
14
14
|
|
|
@@ -80,16 +80,15 @@ REGLE FONDATRICE : suis les règles et ne dévie pas du chemin.
|
|
|
80
80
|
STYLE UTILISE : apporte l'information au plus rapide, sans détours, ni sollicitation à l'utilisateur, ou à des tiers.
|
|
81
81
|
|
|
82
82
|
FORMAT DE RÉPONSE OBLIGATOIRE :
|
|
83
|
-
Ta réponse DOIT être un
|
|
83
|
+
Ta réponse DOIT être un tableau d'objets JSON (Même pour une seule action). Chaque objet doit contenir exactement 2 champs :
|
|
84
84
|
1. "action" (string)
|
|
85
85
|
2. "params" (object)
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
]
|
|
87
|
+
Exemple :
|
|
88
|
+
[
|
|
89
|
+
{ "action": "search_models", "params": {"query": "..."} },
|
|
90
|
+
{ "action": "displayMessage", "params": { "message": "Je cherche les modèles..." } }
|
|
91
|
+
]
|
|
93
92
|
|
|
94
93
|
Tu as accès aux outils et actions suivants.
|
|
95
94
|
|
|
@@ -529,7 +528,7 @@ export async function handleChatRequest(params, user, sendEvent = null) {
|
|
|
529
528
|
}
|
|
530
529
|
|
|
531
530
|
const toolResults = [];
|
|
532
|
-
|
|
531
|
+
const finalActionResults = [];
|
|
533
532
|
|
|
534
533
|
for (const command of commands) {
|
|
535
534
|
logger.debug(`[Assistant] Action décidée par l'IA: ${command.action}`, command);
|
|
@@ -554,27 +553,31 @@ export async function handleChatRequest(params, user, sendEvent = null) {
|
|
|
554
553
|
const res = await Event.Trigger('OnChatAction', 'event', 'user', action, parsedParams, command, llmOptions, user, params);
|
|
555
554
|
|
|
556
555
|
if (res) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
556
|
+
if (res.isToolResult) {
|
|
557
|
+
toolResults.push({ action, result: res.toolResult || res.displayMessage });
|
|
558
|
+
} else {
|
|
559
|
+
// On a trouvé une action finale. On la stocke.
|
|
560
|
+
finalActionResults.push(res);
|
|
561
|
+
}
|
|
560
562
|
} else {
|
|
561
563
|
// Si l'action n'est reconnue par aucune des logiques ci-dessus
|
|
562
564
|
logger.warn(`[Assistant] Action non reconnue reçue de l'IA: ${action}`);
|
|
563
|
-
|
|
565
|
+
finalActionResults.push({
|
|
564
566
|
success: true,
|
|
565
567
|
displayMessage: i18n.t('assistant.unknownAction', "Désolé, je ne comprends pas la commande '{{action}}'.", { action })
|
|
566
|
-
};
|
|
567
|
-
break; // Sortir de la boucle des commandes
|
|
568
|
+
});
|
|
568
569
|
}
|
|
569
570
|
}
|
|
570
571
|
|
|
571
|
-
// Si une
|
|
572
|
-
if (
|
|
572
|
+
// Si une ou plusieurs actions finales ont été exécutées, on retourne le résultat.
|
|
573
|
+
if (finalActionResults.length > 0) {
|
|
574
|
+
// Rétrocompatibilité : si un seul résultat, on renvoie l'objet directement. Sinon, on renvoie un tableau 'results'.
|
|
575
|
+
const response = finalActionResults.length === 1 ? finalActionResults[0] : { success: true, results: finalActionResults };
|
|
573
576
|
if (sendEvent) {
|
|
574
|
-
sendEvent('final_result',
|
|
577
|
+
sendEvent('final_result', response);
|
|
575
578
|
return;
|
|
576
579
|
}
|
|
577
|
-
return
|
|
580
|
+
return response;
|
|
578
581
|
}
|
|
579
582
|
|
|
580
583
|
// Si on a uniquement des résultats d'outils, on les ajoute à l'historique et on continue la boucle.
|
|
@@ -673,7 +676,14 @@ async function handleFinalChatAction(action, params, parsedResponse, llmOptions,
|
|
|
673
676
|
|
|
674
677
|
// Actions nécessitant une confirmation de l'utilisateur
|
|
675
678
|
if (['post', 'update', 'delete'].includes(action)) {
|
|
676
|
-
return {
|
|
679
|
+
return {
|
|
680
|
+
success: true,
|
|
681
|
+
displayMessage: i18n.t('assistant.confirmActionPrompt', "Veuillez confirmer l'action suivante :"),
|
|
682
|
+
confirmationRequest: parsedResponse,
|
|
683
|
+
model: params.model,
|
|
684
|
+
filter: params.filter,
|
|
685
|
+
data: params.data
|
|
686
|
+
};
|
|
677
687
|
}
|
|
678
688
|
|
|
679
689
|
// Action finale pour afficher un message
|
|
@@ -8,7 +8,7 @@ import {downloadFromS3, getUserS3Config, listS3Backups, uploadToS3} from "../buc
|
|
|
8
8
|
import fs from "node:fs";
|
|
9
9
|
import {modelsCache, runCryptoWorkerTask} from "./data.core.js";
|
|
10
10
|
import * as tar from "tar";
|
|
11
|
-
import {getCollection, getUserCollectionName, modelsCollection} from "../mongodb.js";
|
|
11
|
+
import {getCollection, getDatabase, getUserCollectionName, modelsCollection} from "../mongodb.js";
|
|
12
12
|
import {removeFile} from "../file.js";
|
|
13
13
|
import {cancelAlerts, scheduleAlerts} from "./data.scheduling.js";
|
|
14
14
|
import {dbName} from "../../constants.js";
|
|
@@ -136,7 +136,7 @@ export const loadFromDump = async (user, options = {}) => {
|
|
|
136
136
|
await tar.extract({file: backupFilePath, gzip: true, C: tmpRestoreDir, sync: true});
|
|
137
137
|
|
|
138
138
|
// ... (Cleaning logic: deleteMany, removeFile, cancelAlerts) ...
|
|
139
|
-
const datasCollection = getCollection(
|
|
139
|
+
const datasCollection = getCollection(Config.Get('dataCollection', 'datas'));
|
|
140
140
|
if (modelsOnly) {
|
|
141
141
|
await modelsCollection.deleteMany({_user: user.username});
|
|
142
142
|
} else {
|
|
@@ -169,7 +169,7 @@ export const loadFromDump = async (user, options = {}) => {
|
|
|
169
169
|
args.push('--nsInclude', `${d}.models`);
|
|
170
170
|
} else {
|
|
171
171
|
// mongorestore accepte plusieurs fois l'option --nsInclude
|
|
172
|
-
args.push('--nsInclude', `${d}.datas`);
|
|
172
|
+
args.push('--nsInclude', `${d}.${Config.Get('dataCollection', 'datas')}`);
|
|
173
173
|
args.push('--nsInclude', `${d}.models`);
|
|
174
174
|
}
|
|
175
175
|
// Le répertoire source est le dernier argument
|
|
@@ -245,7 +245,7 @@ export const dumpUserData = async (user) => {
|
|
|
245
245
|
|
|
246
246
|
|
|
247
247
|
const d = Config.Get('dbName', dbName);
|
|
248
|
-
const collections = await
|
|
248
|
+
const collections = await getDatabase().listCollections().toArray();
|
|
249
249
|
for (const collection of collections) {
|
|
250
250
|
const collsToBackup = [await getUserCollectionName(user), 'models'];
|
|
251
251
|
if (collsToBackup.includes(collection.name)) {
|
package/src/modules/data/data.js
CHANGED
|
@@ -2,7 +2,7 @@ import {Logger} from "../../gameObject.js";
|
|
|
2
2
|
import {mkdir} from 'node:fs/promises';
|
|
3
3
|
import {onInit as historyInit} from "./data.history.js";
|
|
4
4
|
import {isDemoUser, isLocalUser} from "../../data.js";
|
|
5
|
-
import {install,
|
|
5
|
+
import {install, storageSafetyMargin} from "../../constants.js";
|
|
6
6
|
import {createCollection, getCollection} from "../mongodb.js";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import {isGUID, sequential} from "../../core.js";
|
|
@@ -10,19 +10,19 @@ import {Event} from "../../events.js";
|
|
|
10
10
|
import fs from "node:fs";
|
|
11
11
|
import schedule from "node-schedule";
|
|
12
12
|
import {middleware} from "../../middlewares/middleware-mongodb.js";
|
|
13
|
-
import i18n from "../../i18n.js";
|
|
14
13
|
import checkDiskSpace from "check-disk-space";
|
|
15
14
|
import {removeFile} from "../file.js";
|
|
16
15
|
import {hasPermission} from "../user.js";
|
|
17
|
-
import {
|
|
16
|
+
import { onInit as userInit } from '../user.js';
|
|
18
17
|
import {registerRoutes} from "./data.routes.js";
|
|
19
18
|
import {mongoDBWhitelist} from "./data.core.js";
|
|
20
19
|
import {onInit as relationsInit} from "./data.relations.js";
|
|
21
|
-
import {
|
|
20
|
+
import { onInit as validationInit} from "./data.validation.js";
|
|
22
21
|
import {cancelAlerts, scheduleAlerts, onInit as scheduleInit} from "./data.scheduling.js";
|
|
23
22
|
import {deleteData, installPack, onInit as operationsInit} from "./data.operations.js";
|
|
24
23
|
import {jobDumpUserData, onInit as backupInit} from "./data.backup.js";
|
|
25
24
|
import {Config} from "../../config.js";
|
|
25
|
+
import {profiles} from "../../profiles.js";
|
|
26
26
|
|
|
27
27
|
let engine;
|
|
28
28
|
let logger;
|
|
@@ -57,7 +57,7 @@ export async function onInit(defaultEngine) {
|
|
|
57
57
|
|
|
58
58
|
const i = Config.Get('install', install);
|
|
59
59
|
if( i ) {
|
|
60
|
-
datasCollection = await createCollection(
|
|
60
|
+
datasCollection = await createCollection(Config.Get('dataCollection', 'datas'));
|
|
61
61
|
historyCollection = await createCollection("history");
|
|
62
62
|
filesCollection = await createCollection("files");
|
|
63
63
|
packsCollection = await createCollection("packs");
|
|
@@ -127,7 +127,7 @@ export async function onInit(defaultEngine) {
|
|
|
127
127
|
|
|
128
128
|
}else {
|
|
129
129
|
modelsCollection = getCollection("models");
|
|
130
|
-
datasCollection = getCollection(
|
|
130
|
+
datasCollection = getCollection(Config.Get('dataCollection','datas'));
|
|
131
131
|
filesCollection = getCollection("files");
|
|
132
132
|
packsCollection = getCollection("packs");
|
|
133
133
|
historyCollection = getCollection("history");
|
|
@@ -139,6 +139,7 @@ export async function onInit(defaultEngine) {
|
|
|
139
139
|
validationInit(defaultEngine);
|
|
140
140
|
relationsInit(defaultEngine);
|
|
141
141
|
scheduleInit(defaultEngine);
|
|
142
|
+
userInit(defaultEngine);
|
|
142
143
|
operationsInit(defaultEngine);
|
|
143
144
|
|
|
144
145
|
|
|
@@ -255,7 +256,7 @@ export async function handleDemoInitialization(req, res) {
|
|
|
255
256
|
|
|
256
257
|
try {
|
|
257
258
|
// 1. Nettoyage de l'environnement (inchangé)
|
|
258
|
-
const datasCollection = getCollection("datas");
|
|
259
|
+
const datasCollection = getCollection(Config.Get('dataCollection',"datas"));
|
|
259
260
|
const modelsCollection = getCollection("models");
|
|
260
261
|
const filesCollection = getCollection("files");
|
|
261
262
|
|