data-primals-engine 1.2.3 → 1.2.4
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/CONTRIBUTING.md +91 -0
- package/README.md +33 -17
- package/client/src/App.jsx +0 -5
- package/client/src/ConditionBuilder.scss +34 -1
- package/client/src/ConditionBuilder2.jsx +179 -53
- package/client/src/ContentView.jsx +0 -3
- package/client/src/CronBuilder.jsx +0 -1
- package/client/src/CronPartBuilder.jsx +0 -2
- package/client/src/DashboardView.jsx +0 -5
- package/client/src/DataEditor.jsx +8 -10
- package/client/src/DataLayout.jsx +0 -1
- package/client/src/DataTable.jsx +1 -3
- package/client/src/Field.jsx +0 -5
- package/client/src/FlexBuilder.jsx +1 -1
- package/client/src/ModelCreatorField.jsx +1 -5
- package/client/src/RTE.jsx +1 -6
- package/client/src/RTETrans.jsx +0 -2
- package/client/src/RelationField.jsx +1 -1
- package/client/src/RelationValue.jsx +1 -2
- package/client/src/TourSpotlight.jsx +0 -2
- package/client/src/filter.js +87 -0
- package/client/src/hooks/data.js +1 -3
- package/client/src/hooks/useTutorials.jsx +0 -1
- package/package.json +3 -3
- package/server.js +2 -2
- package/src/email.js +2 -2
- package/src/engine.js +59 -20
- package/src/index.js +1 -1
- package/src/middlewares/middleware-mongodb.js +0 -1
- package/src/modules/assistant.js +1 -3
- package/src/modules/bucket.js +3 -4
- package/src/modules/{data.js → data/data.js} +34 -51
- package/src/modules/data/index.js +1 -0
- package/src/modules/file.js +1 -1
- package/src/modules/mongodb.js +0 -1
- package/src/modules/user.js +1 -1
- package/src/modules/workflow.js +38 -38
- package/src/packs.js +4 -1
- package/test/data.backup.integration.test.js +4 -5
- package/test/data.integration.test.js +2 -6
- package/test/events.test.js +1 -1
- package/test/file.test.js +1 -4
- package/test/import_export.integration.test.js +20 -13
- package/test/model.integration.test.js +8 -10
- package/test/user.test.js +2 -2
- package/test/vm.test.js +1 -1
- package/test/workflow.integration.test.js +17 -14
- package/test/workflow.robustness.test.js +15 -10
- package/src/modules/test +0 -147
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {Logger} from "
|
|
1
|
+
import {Logger} from "../../gameObject.js";
|
|
2
2
|
import {BSON, ObjectId} from "mongodb";
|
|
3
3
|
import * as util from 'node:util';
|
|
4
4
|
import {promisify} from 'node:util';
|
|
@@ -9,9 +9,9 @@ import * as tar from "tar";
|
|
|
9
9
|
import process from "node:process";
|
|
10
10
|
import {randomColor} from "randomcolor";
|
|
11
11
|
import cronstrue from 'cronstrue/i18n.js';
|
|
12
|
-
import {setTimeoutMiddleware} from '
|
|
12
|
+
import {setTimeoutMiddleware} from '../../middlewares/timeout.js';
|
|
13
13
|
import {mkdir} from 'node:fs/promises';
|
|
14
|
-
import {anonymizeText, getDefaultForType, getFieldValueHash, getUserId, isDemoUser, isLocalUser} from "
|
|
14
|
+
import {anonymizeText, getDefaultForType, getFieldValueHash, getUserId, isDemoUser, isLocalUser} from "../../data.js";
|
|
15
15
|
import {
|
|
16
16
|
allowedFields,
|
|
17
17
|
dbName,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
optionsSanitizer,
|
|
37
37
|
searchRequestTimeout,
|
|
38
38
|
storageSafetyMargin
|
|
39
|
-
} from "
|
|
39
|
+
} from "../../constants.js";
|
|
40
40
|
import {
|
|
41
41
|
getCollection,
|
|
42
42
|
getCollectionForUser,
|
|
@@ -44,43 +44,43 @@ import {
|
|
|
44
44
|
isObjectId,
|
|
45
45
|
modelsCollection,
|
|
46
46
|
packsCollection
|
|
47
|
-
} from "
|
|
48
|
-
import {dbUrl, MongoClient, MongoDatabase} from "
|
|
47
|
+
} from "../mongodb.js";
|
|
48
|
+
import {dbUrl, MongoClient, MongoDatabase} from "../../engine.js";
|
|
49
49
|
import path from "node:path";
|
|
50
|
-
import {getFileExtension, getObjectHash, getRandom, isGUID, isPlainObject, randomDate, sleep, uuidv4} from "
|
|
51
|
-
import {Event} from "
|
|
50
|
+
import {getFileExtension, getObjectHash, getRandom, isGUID, isPlainObject, randomDate, sleep, uuidv4} from "../../core.js";
|
|
51
|
+
import {Event} from "../../events.js";
|
|
52
52
|
import fs from "node:fs";
|
|
53
53
|
import schedule from "node-schedule";
|
|
54
|
-
import {middleware} from "
|
|
55
|
-
import i18n from "
|
|
54
|
+
import {middleware} from "../../middlewares/middleware-mongodb.js";
|
|
55
|
+
import i18n from "../../i18n.js";
|
|
56
56
|
import {
|
|
57
57
|
executeSafeJavascript, processWorkflowRun,
|
|
58
58
|
runScheduledJobWithDbLock,
|
|
59
59
|
scheduleWorkflowTriggers,
|
|
60
60
|
triggerWorkflows
|
|
61
|
-
} from "
|
|
61
|
+
} from "../workflow.js";
|
|
62
62
|
import NodeCache from "node-cache";
|
|
63
63
|
import AWS from 'aws-sdk';
|
|
64
|
-
import {openaiJobModel} from "
|
|
64
|
+
import {openaiJobModel} from "../../openai.jobs.js";
|
|
65
65
|
import checkDiskSpace from "check-disk-space";
|
|
66
66
|
import {fileURLToPath} from 'url';
|
|
67
67
|
import {Worker} from 'worker_threads';
|
|
68
|
-
import {addFile, encryptFile, removeFile} from "
|
|
69
|
-
import {downloadFromS3, getS3Stream, getUserS3Config, listS3Backups, uploadToS3} from "
|
|
68
|
+
import {addFile, encryptFile, removeFile} from "../file.js";
|
|
69
|
+
import {downloadFromS3, getS3Stream, getUserS3Config, listS3Backups, uploadToS3} from "../bucket.js";
|
|
70
70
|
import {
|
|
71
71
|
calculateTotalUserStorageUsage,
|
|
72
72
|
generateLimiter,
|
|
73
73
|
hasPermission,
|
|
74
74
|
middlewareAuthenticator,
|
|
75
75
|
userInitiator
|
|
76
|
-
} from "
|
|
77
|
-
import {assistantGlobalLimiter} from "
|
|
78
|
-
import {getAllPacks} from "
|
|
79
|
-
import {throttleMiddleware} from "
|
|
80
|
-
import {Config} from "
|
|
81
|
-
import {profiles} from "
|
|
82
|
-
import {processFilterPlaceholders} from "
|
|
83
|
-
import {tutorialsConfig} from "
|
|
76
|
+
} from "../user.js";
|
|
77
|
+
import {assistantGlobalLimiter} from "../assistant.js";
|
|
78
|
+
import {getAllPacks} from "../../packs.js";
|
|
79
|
+
import {throttleMiddleware} from "../../middlewares/throttle.js";
|
|
80
|
+
import {Config} from "../../config.js";
|
|
81
|
+
import {profiles} from "../../../client/src/constants.js";
|
|
82
|
+
import {processFilterPlaceholders} from "../../../client/src/filter.js";
|
|
83
|
+
import {tutorialsConfig} from "../../../client/src/tutorials.js";
|
|
84
84
|
|
|
85
85
|
// Obtenir le chemin du répertoire courant de manière fiable avec ES Modules
|
|
86
86
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -202,7 +202,6 @@ export const dataTypes = {
|
|
|
202
202
|
},
|
|
203
203
|
code: {
|
|
204
204
|
validate: (value, field) => {
|
|
205
|
-
console.log(field,typeof(value));
|
|
206
205
|
return value === null || (field.language === 'json' && typeof(value) === 'object') || (typeof value === 'string' && (field.maxlength === undefined || field.maxlength <= 0 || value.length <= field.maxlength));
|
|
207
206
|
},
|
|
208
207
|
filter: async (value, field) => {
|
|
@@ -536,7 +535,7 @@ export const getAPILang = (langs) => {
|
|
|
536
535
|
*/
|
|
537
536
|
function runImportExportWorker(action, payload) {
|
|
538
537
|
return new Promise((resolve, reject) => {
|
|
539
|
-
const workerPath = path.resolve(
|
|
538
|
+
const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
|
|
540
539
|
const worker = new Worker(workerPath);
|
|
541
540
|
|
|
542
541
|
worker.postMessage({ action, payload });
|
|
@@ -571,7 +570,7 @@ function runImportExportWorker(action, payload) {
|
|
|
571
570
|
*/
|
|
572
571
|
function runCryptoWorkerTask(action, payload) {
|
|
573
572
|
return new Promise((resolve, reject) => {
|
|
574
|
-
const workerPath = path.resolve(
|
|
573
|
+
const workerPath = path.resolve(process.cwd(), './src/workers/crypto-worker.js');
|
|
575
574
|
const worker = new Worker(workerPath);
|
|
576
575
|
|
|
577
576
|
worker.postMessage({ action, payload });
|
|
@@ -1296,7 +1295,7 @@ export async function onInit(defaultEngine) {
|
|
|
1296
1295
|
"$rand",
|
|
1297
1296
|
"$abs", '$sin', '$cos', '$tan', '$asin', '$acos', '$atan',
|
|
1298
1297
|
"$toDate", "$toBool", "$toString", "$toInt", "$toDouble",
|
|
1299
|
-
"$dateSubtract", "$dateAdd", "$dateToString",
|
|
1298
|
+
"$dateDiff", "$dateSubtract", "$dateAdd", "$dateToString",
|
|
1300
1299
|
'$year', '$month', '$week', '$dayOfMonth', '$dayOfWeek', '$dayOfYear', '$hour', '$minute', '$second', '$millisecond'
|
|
1301
1300
|
]}));
|
|
1302
1301
|
|
|
@@ -2014,7 +2013,6 @@ export async function onInit(defaultEngine) {
|
|
|
2014
2013
|
.limit(maxModelsPerUser).toArray());
|
|
2015
2014
|
res.json(models);
|
|
2016
2015
|
} catch (error) {
|
|
2017
|
-
console.log(error);
|
|
2018
2016
|
logger.error(error);
|
|
2019
2017
|
res.status(500).json({ success: false, error: error.message });
|
|
2020
2018
|
}
|
|
@@ -2340,15 +2338,6 @@ export async function onInit(defaultEngine) {
|
|
|
2340
2338
|
res.json({ success: true, value: resultValue, totalCount: totalCount });
|
|
2341
2339
|
|
|
2342
2340
|
} catch (error) { // <--- CATCH PRINCIPAL
|
|
2343
|
-
// --- Log de l'erreur BRUTE interceptée ---
|
|
2344
|
-
console.log('--- CATCH PRINCIPAL - RAW ERROR OBJECT ---');
|
|
2345
|
-
console.log('Type:', typeof error);
|
|
2346
|
-
console.log('Instance of Error:', error instanceof Error);
|
|
2347
|
-
console.log('Error Object:', error); // Affiche la structure brute
|
|
2348
|
-
console.log('Error Message:', error?.message);
|
|
2349
|
-
console.log('Error Stack:', error?.stack);
|
|
2350
|
-
console.log('--- END CATCH PRINCIPAL ---');
|
|
2351
|
-
// --- Fin Log ---
|
|
2352
2341
|
|
|
2353
2342
|
// Tentative de log via le logger standard (qui peut encore échouer si l'erreur est vraiment étrange)
|
|
2354
2343
|
try {
|
|
@@ -3244,7 +3233,8 @@ async function processDocuments(datas, model, collection, me) {
|
|
|
3244
3233
|
const idMap = new Map();
|
|
3245
3234
|
const allInsertedIds = [];
|
|
3246
3235
|
|
|
3247
|
-
|
|
3236
|
+
const realData = Event.Trigger("OnDataInsert", "event", "system", datas) || datas;
|
|
3237
|
+
for (const doc of realData) {
|
|
3248
3238
|
try {
|
|
3249
3239
|
const newDocId = await insertAndResolveRelations(doc, model, collection, me, idMap);
|
|
3250
3240
|
if (newDocId) {
|
|
@@ -3445,10 +3435,12 @@ async function applyFieldFilters(docToProcess, model) {
|
|
|
3445
3435
|
for (const field of model.fields) {
|
|
3446
3436
|
docToProcess[field.name] = typeof(docToProcess[field.name]) === 'undefined' || docToProcess[field.name] === null ? field.default:docToProcess[field.name];
|
|
3447
3437
|
if (dataTypes[field.type]?.filter) {
|
|
3448
|
-
|
|
3438
|
+
const filter = await dataTypes[field.type].filter(
|
|
3449
3439
|
docToProcess[field.name],
|
|
3450
3440
|
field
|
|
3451
3441
|
);
|
|
3442
|
+
const realFilter = Event.Trigger('OnDataFilter', "event", "system",filter, field, docToProcess );
|
|
3443
|
+
docToProcess[field.name] = realFilter || filter;
|
|
3452
3444
|
}
|
|
3453
3445
|
}
|
|
3454
3446
|
}
|
|
@@ -3478,7 +3470,9 @@ function validateModelData(doc, model, isPatch = false) {
|
|
|
3478
3470
|
if (!fieldDef) continue; // On ignore les champs supplémentaires
|
|
3479
3471
|
|
|
3480
3472
|
const validator = dataTypes[fieldDef.type]?.validate;
|
|
3481
|
-
|
|
3473
|
+
const valid = validator && validator(value, fieldDef);
|
|
3474
|
+
const realValidation = Event.Trigger('OnDataValidate', "event", "system", value, fieldDef,doc );
|
|
3475
|
+
if (!(valid || realValidation)) {
|
|
3482
3476
|
throw new Error(i18n.t('api.field.validationFailed', { field: fieldName, value }));
|
|
3483
3477
|
}
|
|
3484
3478
|
}
|
|
@@ -3694,8 +3688,6 @@ export const editData = async (modelName, filter, data, files, user, triggerWork
|
|
|
3694
3688
|
return await internalEditOrPatchData(modelName, filter, data, files, user, false, triggerWorkflow, waitForWorkflow);
|
|
3695
3689
|
};
|
|
3696
3690
|
|
|
3697
|
-
// Dans src/modules/data.js
|
|
3698
|
-
|
|
3699
3691
|
const internalEditOrPatchData = async (modelName, filter, data, files, user, isPatch, triggerWorkflow = true, waitForWorkflow = false) => {
|
|
3700
3692
|
try {
|
|
3701
3693
|
// 1. Vérification des permissions
|
|
@@ -3949,7 +3941,6 @@ export const deleteData = async (modelName, filter, user ={}, triggerWorkflow, w
|
|
|
3949
3941
|
|
|
3950
3942
|
}
|
|
3951
3943
|
|
|
3952
|
-
console.log(util.inspect(findFilter, false, 8, true));
|
|
3953
3944
|
// 2. Récupérer les documents à supprimer pour vérifier leur type et annuler les schedules
|
|
3954
3945
|
const documentsToDelete = await collection.aggregate([{ $match: { $expr: { "$and": findFilter } } }]).toArray();
|
|
3955
3946
|
|
|
@@ -4130,8 +4121,6 @@ export const deleteData = async (modelName, filter, user ={}, triggerWorkflow, w
|
|
|
4130
4121
|
}
|
|
4131
4122
|
}
|
|
4132
4123
|
|
|
4133
|
-
// ... (le reste du fichier data.js)
|
|
4134
|
-
|
|
4135
4124
|
export const searchData = async (query, user) => {
|
|
4136
4125
|
const { page, limit, sort, model, ids, timeout, pack } = query; // Les filtres de la requête (attention aux injections MongoDB !)
|
|
4137
4126
|
|
|
@@ -4555,7 +4544,6 @@ export const searchData = async (query, user) => {
|
|
|
4555
4544
|
} else if (fi.type === 'array') {
|
|
4556
4545
|
// Handle array filtering here
|
|
4557
4546
|
if (data[fi.name]) {
|
|
4558
|
-
console.log('array filtering', data[fi.name]);
|
|
4559
4547
|
pipelines.push({
|
|
4560
4548
|
$match: {
|
|
4561
4549
|
$expr: {
|
|
@@ -4591,7 +4579,6 @@ export const searchData = async (query, user) => {
|
|
|
4591
4579
|
|
|
4592
4580
|
let pipelines = [];
|
|
4593
4581
|
if( allIds.length ){
|
|
4594
|
-
console.log({allIds});
|
|
4595
4582
|
const id = {$in: ["$_id", allIds.map(m => new ObjectId(m))]};
|
|
4596
4583
|
pipelines.push({
|
|
4597
4584
|
$match: { $expr: id }
|
|
@@ -4617,7 +4604,7 @@ export const searchData = async (query, user) => {
|
|
|
4617
4604
|
pipelines.push({$project: {_model: 0}});
|
|
4618
4605
|
}
|
|
4619
4606
|
|
|
4620
|
-
console.log(util.inspect(pipelines, false, 29, true));
|
|
4607
|
+
//console.log(util.inspect(pipelines, false, 29, true));
|
|
4621
4608
|
|
|
4622
4609
|
// 4. Exécuter la pipeline
|
|
4623
4610
|
const ts = parseInt(timeout, 10)/2.0 || searchRequestTimeout;
|
|
@@ -5787,7 +5774,6 @@ export async function installPack(packId, user, lang) {
|
|
|
5787
5774
|
if (documents.length === 0) continue;
|
|
5788
5775
|
|
|
5789
5776
|
const docsToInsert = [];
|
|
5790
|
-
console.log(modelName, user);
|
|
5791
5777
|
|
|
5792
5778
|
const modelDefForHash = await getModel(modelName, user);
|
|
5793
5779
|
|
|
@@ -5921,10 +5907,7 @@ export async function installPack(packId, user, lang) {
|
|
|
5921
5907
|
|
|
5922
5908
|
|
|
5923
5909
|
export const installAllPacks = async () => {
|
|
5924
|
-
|
|
5925
5910
|
const packs = await getAllPacks();
|
|
5926
|
-
|
|
5927
|
-
console.log(util.inspect(packs, false, 20, true));
|
|
5928
5911
|
await packsCollection.deleteMany({ _user: { $exists : false }});
|
|
5929
5912
|
await packsCollection.insertMany(packs);
|
|
5930
5913
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './data.js';
|
package/src/modules/file.js
CHANGED
|
@@ -8,7 +8,7 @@ import {getFileExtension, isGUID, uuidv4} from "../core.js";
|
|
|
8
8
|
import path from "node:path";
|
|
9
9
|
import process from "node:process";
|
|
10
10
|
import fs from "node:fs";
|
|
11
|
-
import { checkServerCapacity} from "./data.js";
|
|
11
|
+
import { checkServerCapacity} from "./data/index.js";
|
|
12
12
|
import crypto from "node:crypto";
|
|
13
13
|
import * as tar from "tar";
|
|
14
14
|
import {promisify} from "node:util";
|
package/src/modules/mongodb.js
CHANGED
package/src/modules/user.js
CHANGED
|
@@ -3,7 +3,7 @@ import {MongoDatabase} from "../engine.js";
|
|
|
3
3
|
import {getCollection, getCollectionForUser, getUserCollectionName} from "./mongodb.js";
|
|
4
4
|
import {isLocalUser} from "../data.js";
|
|
5
5
|
import {ObjectId} from "mongodb";
|
|
6
|
-
import {getAPILang} from "./data.js";
|
|
6
|
+
import {getAPILang} from "./data/index.js";
|
|
7
7
|
import {Logger} from "../gameObject.js";
|
|
8
8
|
import rateLimit from "express-rate-limit";
|
|
9
9
|
|
package/src/modules/workflow.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// Exemple conceptuel dans la fonction de planification
|
|
2
1
|
import {getCollection, getCollectionForUser, isObjectId} from "./mongodb.js";
|
|
3
2
|
import schedule from "node-schedule";
|
|
4
3
|
import {ObjectId} from "mongodb";
|
|
@@ -7,16 +6,16 @@ import crypto from "node:crypto";
|
|
|
7
6
|
import ivm from 'isolated-vm';
|
|
8
7
|
|
|
9
8
|
import {Logger} from "../gameObject.js";
|
|
10
|
-
import {deleteData, insertData, patchData, searchData} from "./data.js";
|
|
11
|
-
import {emailDefaultConfig, maxExecutionsByStep, maxWorkflowSteps
|
|
9
|
+
import {deleteData, insertData, patchData, searchData} from "./data/index.js";
|
|
10
|
+
import {emailDefaultConfig, maxExecutionsByStep, maxWorkflowSteps} from "../constants.js";
|
|
12
11
|
import {ChatOpenAI} from "@langchain/openai";
|
|
13
12
|
import {ChatGoogleGenerativeAI} from "@langchain/google-genai";
|
|
14
13
|
import {ChatPromptTemplate} from "@langchain/core/prompts";
|
|
14
|
+
import { ChatDeepSeek } from "@langchain/deepseek";
|
|
15
15
|
import i18n from "../../src/i18n.js";
|
|
16
16
|
import {sendEmail} from "../email.js";
|
|
17
17
|
|
|
18
18
|
import * as workflowModule from './workflow.js';
|
|
19
|
-
import {isConditionMet} from "../filter.js";
|
|
20
19
|
import util from "node:util";
|
|
21
20
|
|
|
22
21
|
let logger = null;
|
|
@@ -1174,7 +1173,6 @@ export async function triggerWorkflows(triggerData, user, eventType) {
|
|
|
1174
1173
|
|
|
1175
1174
|
|
|
1176
1175
|
console.debug(`[Workflow Trigger] Vérification dataFilter pour trigger ${trigger._id} avec filtre combiné:`, JSON.stringify(finalFilter));
|
|
1177
|
-
console.log({triggerData, finalFilter});
|
|
1178
1176
|
const match = await searchData({ model: triggerData._model, filter: finalFilter, limit: 1 }, user);
|
|
1179
1177
|
if (!match.count) {
|
|
1180
1178
|
console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter non satisfait par la donnée ${dataId}. WorkflowRun non créé.`);
|
|
@@ -1358,7 +1356,6 @@ export async function processWorkflowRun(workflowRunId, user) {
|
|
|
1358
1356
|
if (!isObjectId(actionId)) continue;
|
|
1359
1357
|
const actionDef = await dbCollection.findOne({ _id: new ObjectId(actionId), _model: 'workflowAction' });
|
|
1360
1358
|
if (!actionDef) return await logError(`Action definition ${actionId} not found.`);
|
|
1361
|
-
console.log({actionDef});
|
|
1362
1359
|
const actionResult = await workflowModule.executeStepAction(actionDef, contextData, user, dbCollection);
|
|
1363
1360
|
|
|
1364
1361
|
if (actionResult.status === 'paused') {
|
|
@@ -1403,7 +1400,7 @@ export async function processWorkflowRun(workflowRunId, user) {
|
|
|
1403
1400
|
if (actionResult.updatedContext) {
|
|
1404
1401
|
contextData = { ...contextData, ...actionResult.updatedContext };
|
|
1405
1402
|
}
|
|
1406
|
-
console.log("action", util.inspect(actionResult, false, 8, true));
|
|
1403
|
+
//console.log("action", util.inspect(actionResult, false, 8, true));
|
|
1407
1404
|
logger.info(`[processWorkflowRun] Run ID: ${runId}, Step ID: ${currentStepId}, Action ID: ${actionId}: Executed successfully.`);
|
|
1408
1405
|
}
|
|
1409
1406
|
}
|
|
@@ -1468,104 +1465,107 @@ export async function processWorkflowRun(workflowRunId, user) {
|
|
|
1468
1465
|
}
|
|
1469
1466
|
}
|
|
1470
1467
|
/**
|
|
1471
|
-
*
|
|
1472
|
-
*
|
|
1473
|
-
*
|
|
1474
|
-
*
|
|
1468
|
+
* Executes an AI content generation action ('GenerateAIContent').
|
|
1469
|
+
* Retrieves the API key (prioritizing the user's environment), initializes a LangChain client,
|
|
1470
|
+
* formats a prompt with context data, calls the LLM, and returns the result
|
|
1471
|
+
* to be added to the workflow context.
|
|
1475
1472
|
*
|
|
1476
|
-
* @param {object} action -
|
|
1477
|
-
* @param {object} context -
|
|
1478
|
-
* @param {object} user -
|
|
1473
|
+
* @param {object} action - The action definition from the workflow.
|
|
1474
|
+
* @param {object} context - The current workflow execution context.
|
|
1475
|
+
* @param {object} user - The user executing the workflow.
|
|
1479
1476
|
* @returns {Promise<{success: boolean, updatedContext?: object, message?: string}>}
|
|
1480
1477
|
*/
|
|
1481
1478
|
async function executeGenerateAIContentAction(action, context, user) {
|
|
1482
1479
|
const { aiProvider, aiModel, prompt } = action;
|
|
1483
1480
|
|
|
1484
|
-
// 1.
|
|
1481
|
+
// 1. Retrieve the API key (User Environment > Machine Environment)
|
|
1485
1482
|
let apiKey;
|
|
1486
1483
|
|
|
1487
1484
|
const providers = {
|
|
1488
1485
|
"OpenAI" : "OPENAI_API_KEY",
|
|
1489
|
-
"Google": "GOOGLE_API_KEY"
|
|
1486
|
+
"Google": "GOOGLE_API_KEY",
|
|
1487
|
+
"DeepSeek": "DEEPSEEK_API_KEY"
|
|
1490
1488
|
}
|
|
1491
1489
|
const envKeyName = providers[aiProvider];
|
|
1492
1490
|
if( !envKeyName ) {
|
|
1493
|
-
return {success: false, message: i18n.t('aiContent.env', `
|
|
1491
|
+
return {success: false, message: i18n.t('aiContent.env', `API key for provider ${aiProvider} (${envKeyName}) not found in user environment.`)};
|
|
1494
1492
|
}
|
|
1495
1493
|
|
|
1496
|
-
//
|
|
1494
|
+
// First look in the user's environment variables
|
|
1497
1495
|
const envCollection = await getCollectionForUser(user);
|
|
1498
1496
|
const userEnvVar = await envCollection.findOne({ _model: 'env', name: envKeyName, _user: user.username });
|
|
1499
1497
|
|
|
1500
1498
|
if (userEnvVar && userEnvVar.value) {
|
|
1501
1499
|
apiKey = userEnvVar.value;
|
|
1502
|
-
logger.debug(`[AI Action]
|
|
1500
|
+
logger.debug(`[AI Action] Using user environment API key for ${aiProvider}.`);
|
|
1503
1501
|
} else {
|
|
1504
1502
|
apiKey = process.env[envKeyName];
|
|
1505
|
-
logger.debug(`[AI Action]
|
|
1503
|
+
logger.debug(`[AI Action] Using machine environment API key for ${aiProvider}.`);
|
|
1506
1504
|
}
|
|
1507
1505
|
|
|
1508
1506
|
if (!apiKey) {
|
|
1509
|
-
const message = `
|
|
1507
|
+
const message = `API key for ${aiProvider} (${envKeyName}) not found in user or machine environment.`;
|
|
1510
1508
|
logger.error(`[AI Action] ${message}`);
|
|
1511
1509
|
return { success: false, message };
|
|
1512
1510
|
}
|
|
1513
1511
|
|
|
1514
|
-
// 2.
|
|
1512
|
+
// 2. Initialize the LLM client with LangChain
|
|
1515
1513
|
let llm;
|
|
1516
1514
|
try {
|
|
1517
1515
|
switch (aiProvider) {
|
|
1518
1516
|
case 'OpenAI':
|
|
1519
|
-
llm = new ChatOpenAI({ apiKey,
|
|
1517
|
+
llm = new ChatOpenAI({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1520
1518
|
break;
|
|
1521
|
-
case '
|
|
1522
|
-
llm = new ChatGoogleGenerativeAI({ apiKey,
|
|
1519
|
+
case 'Google':
|
|
1520
|
+
llm = new ChatGoogleGenerativeAI({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1521
|
+
break;
|
|
1522
|
+
case 'DeepSeek':
|
|
1523
|
+
llm = new ChatDeepSeek({ apiKey, model: aiModel, temperature: 0.7 });
|
|
1523
1524
|
break;
|
|
1524
1525
|
default:
|
|
1525
|
-
throw new Error(`
|
|
1526
|
+
throw new Error(`Unsupported AI provider: ${aiProvider}`);
|
|
1526
1527
|
}
|
|
1527
1528
|
} catch (initError) {
|
|
1528
|
-
const message =
|
|
1529
|
+
const message = `Failed to initialize AI client for ${aiProvider}: ${initError.message}`;
|
|
1529
1530
|
logger.error(`[AI Action] ${message}`);
|
|
1530
1531
|
return { success: false, message };
|
|
1531
1532
|
}
|
|
1532
1533
|
|
|
1533
1534
|
try {
|
|
1534
1535
|
const substitutedPrompt = await substituteVariables(prompt, context, user);
|
|
1535
|
-
// 3.
|
|
1536
|
-
// LangChain
|
|
1536
|
+
// 3. Create the "Prompt Template"
|
|
1537
|
+
// LangChain handles variable substitution like {triggerData.name}
|
|
1537
1538
|
const realPrompt = ChatPromptTemplate.fromTemplate(substitutedPrompt);
|
|
1538
1539
|
|
|
1539
|
-
// 4.
|
|
1540
|
+
// 4. Create the processing chain (Prompt + Model)
|
|
1540
1541
|
const chain = realPrompt.pipe(llm);
|
|
1541
1542
|
|
|
1542
|
-
// 5.
|
|
1543
|
-
// LangChain
|
|
1544
|
-
logger.debug(`[AI Action]
|
|
1543
|
+
// 5. Invoke the chain with the complete context
|
|
1544
|
+
// LangChain will automatically replace placeholders in the prompt.
|
|
1545
|
+
logger.debug(`[AI Action] Invoking AI with model ${aiModel}.`);
|
|
1545
1546
|
const response = await chain.invoke(context);
|
|
1546
1547
|
|
|
1547
|
-
// 6.
|
|
1548
|
+
// 6. Prepare the result to be merged into the workflow context
|
|
1548
1549
|
const llmOutput = response.content;
|
|
1549
1550
|
const outputVariable = 'aiContent';
|
|
1550
1551
|
const updatedContext = {
|
|
1551
1552
|
[outputVariable]: llmOutput
|
|
1552
1553
|
};
|
|
1553
1554
|
|
|
1554
|
-
logger.info(`[AI Action]
|
|
1555
|
+
logger.info(`[AI Action] Content generated successfully and stored in context variable '${outputVariable}'.`);
|
|
1555
1556
|
|
|
1556
1557
|
return {
|
|
1557
1558
|
success: true,
|
|
1558
|
-
updatedContext //
|
|
1559
|
+
updatedContext // This object will be merged into the main context by the workflow engine
|
|
1559
1560
|
};
|
|
1560
1561
|
|
|
1561
1562
|
} catch (llmError) {
|
|
1562
|
-
const message = `
|
|
1563
|
+
const message = `Error during AI content generation with ${aiProvider}: ${llmError.message}`;
|
|
1563
1564
|
logger.error(`[AI Action] ${message}`, llmError.stack);
|
|
1564
1565
|
return { success: false, message };
|
|
1565
1566
|
}
|
|
1566
1567
|
}
|
|
1567
1568
|
|
|
1568
|
-
|
|
1569
1569
|
/**
|
|
1570
1570
|
* Gère l'action d'envoi d'e-mail d'un workflow.
|
|
1571
1571
|
* Cette version améliorée peut traiter une liste de destinataires, en envoyant un e-mail
|
package/src/packs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {getModels} from "./modules/data.js";
|
|
1
|
+
import {getModels} from "./modules/data/index.js";
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
/*
|
|
@@ -1360,6 +1360,9 @@ return { processedChunk: context.result.chunk };
|
|
|
1360
1360
|
},{
|
|
1361
1361
|
name: "GOOGLE_API_KEY",
|
|
1362
1362
|
value: "demo"
|
|
1363
|
+
},{
|
|
1364
|
+
name: "DEEPSEEK_API_KEY",
|
|
1365
|
+
value: "demo"
|
|
1363
1366
|
}]
|
|
1364
1367
|
}
|
|
1365
1368
|
}
|
|
@@ -10,20 +10,19 @@ import { vi } from 'vitest'
|
|
|
10
10
|
import {
|
|
11
11
|
createModel,
|
|
12
12
|
insertData
|
|
13
|
-
} from '
|
|
13
|
+
} from '../src/index.js';
|
|
14
14
|
|
|
15
15
|
import {
|
|
16
16
|
modelsCollection as getAppModelsCollection,
|
|
17
17
|
getCollectionForUser as getAppUserCollection, getCollectionForUser
|
|
18
|
-
} from '
|
|
18
|
+
} from '../src/modules/mongodb.js';
|
|
19
19
|
import process from "node:process";
|
|
20
20
|
|
|
21
|
-
import { dumpUserData, loadFromDump } from '
|
|
21
|
+
import { dumpUserData, loadFromDump } from '../src/index.js';
|
|
22
22
|
import fs from "node:fs";
|
|
23
23
|
import {initEngine} from "../src/setenv.js";
|
|
24
|
-
import {getUserCollectionName} from "../src/modules/mongodb.js";
|
|
25
24
|
|
|
26
|
-
vi.mock('
|
|
25
|
+
vi.mock('../src/engine', async(importOriginal) => {
|
|
27
26
|
const mod = await importOriginal() // type is inferred
|
|
28
27
|
return {
|
|
29
28
|
...mod
|
|
@@ -7,19 +7,15 @@ import {
|
|
|
7
7
|
editData,
|
|
8
8
|
deleteData,
|
|
9
9
|
searchData, installPack, deleteModels, createModel, patchData
|
|
10
|
-
} from '
|
|
10
|
+
} from '../src/index.js';
|
|
11
11
|
|
|
12
12
|
import {
|
|
13
13
|
modelsCollection as getAppModelsCollection,
|
|
14
14
|
getCollection,
|
|
15
15
|
getCollectionForUser as getAppUserCollection, getCollectionForUser
|
|
16
|
-
} from '
|
|
16
|
+
} from '../src/modules/mongodb.js';
|
|
17
17
|
import {getRandom} from "../src/core.js";
|
|
18
18
|
import {generateUniqueName, initEngine} from "../src/setenv.js";
|
|
19
|
-
import {processWorkflowRun} from "data-primals-engine/modules/workflow";
|
|
20
|
-
import {sendEmail} from "../src/email.js";
|
|
21
|
-
import {MongoDatabase} from "../src/engine.js";
|
|
22
|
-
import {getUserCollectionName} from "../src/modules/mongodb.js";
|
|
23
19
|
|
|
24
20
|
let testModelsColInstance;
|
|
25
21
|
let testDatasColInstance;
|
package/test/events.test.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// events.test.js
|
|
2
2
|
import { Event } from '../src/events.js';
|
|
3
3
|
import {vitest} from "vitest";
|
|
4
|
-
import {expect, describe, it, beforeEach
|
|
4
|
+
import {expect, describe, it, beforeEach} from 'vitest';
|
|
5
5
|
|
|
6
6
|
describe('Event System', () => {
|
|
7
7
|
beforeEach(() => {
|
package/test/file.test.js
CHANGED
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
|
|
2
|
-
import { expect, describe, it, beforeEach, afterEach, beforeAll, afterAll, vi } from 'vitest';
|
|
1
|
+
import { expect, describe, it, beforeEach, beforeAll, afterAll, vi } from 'vitest';
|
|
3
2
|
import { Config } from '../src/config.js';
|
|
4
3
|
import fs from 'node:fs';
|
|
5
4
|
import path from 'node:path';
|
|
6
5
|
import { fileURLToPath } from 'node:url';
|
|
7
|
-
import { ObjectId } from 'mongodb';
|
|
8
6
|
import { initEngine, generateUniqueName } from "../src/setenv.js";
|
|
9
7
|
import { addFile, removeFile, getFile, onInit } from "../src/modules/file.js";
|
|
10
8
|
import {getCollection, getUserCollectionName} from "../src/modules/mongodb.js";
|
|
11
9
|
import { Logger } from "../src/gameObject.js";
|
|
12
10
|
import {maxPrivateFileSize} from "../src/constants.js";
|
|
13
|
-
import {getCollectionForUser} from "data-primals-engine/modules/mongodb";
|
|
14
11
|
|
|
15
12
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
13
|
|