data-primals-engine 1.6.2-rc1 → 1.6.2

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/package.json CHANGED
@@ -1,142 +1,145 @@
1
- {
2
- "name": "data-primals-engine",
3
- "version": "1.6.2-rc1",
4
- "description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
5
- "main": "src/engine.js",
6
- "type": "module",
7
- "scripts": {
8
- "preinstall": "npx force-resolutions",
9
- "dev": "concurrently \"npm:client\" \"npm:devserver\"",
10
- "prod": "npm run build-server && npm run server",
11
- "client": "cd client && npm run dev",
12
- "build-server": "vite build client --config client/vite.config.js --outDir dist",
13
- "build-client": "cd client && npm run build",
14
- "server": "cross-env NODE_ENV=production node server.js",
15
- "devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
16
- "lint": "eslint .",
17
- "format": "prettier --write .",
18
- "clean": "rm -rf node_modules package-lock.json",
19
- "test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism --no-watch",
20
- "audit": "npm audit --audit-level=high",
21
- "migrate:create": "node src/migrate.js create",
22
- "migrate:up": "node src/migrate.js up",
23
- "migrate:down": "node src/migrate.js down",
24
- "migrate:revert": "node src/migrate.js revert",
25
- "migrate:status": "node src/migrate.js status"
26
- },
27
- "optionalDependencies": {
28
- "@rollup/rollup-linux-x64-gnu": "4.6.1"
29
- },
30
- "resolutions": {
31
- "on-headers": "1.1.0",
32
- "brace-expansion": "2.0.2",
33
- "prismjs": "1.30.0",
34
- "xml2js": ">=0.5.0",
35
- "tar-fs": ">=3.1.1",
36
- "vite": ">=7.0.8"
37
- },
38
- "overrides": {
39
- "react-syntax-highlighter": {
40
- "prismjs": "1.30.0"
41
- },
42
- "react-code-blocks": {
43
- "prismjs": "1.30.0"
44
- },
45
- "refractor": {
46
- "prismjs": "1.30.0"
47
- }
48
- },
49
- "repository": {
50
- "type": "git",
51
- "url": "https://github.com/anonympins/data-primals-engine.git"
52
- },
53
- "exports": {
54
- ".": "./src/index.js",
55
- "./modules/*.js": "./src/modules/*.js",
56
- "./modules/*": "./src/modules/*/index.js",
57
- "./client": "./client/index.js",
58
- "./*": "./src/*.js"
59
- },
60
- "dependencies": {
61
- "@langchain/core": "^0.3.79",
62
- "archiver": "^7.0.1",
63
- "aws-sdk": "^2.1692.0",
64
- "bcrypt": "^6.0.0",
65
- "body-parser": "^2.2.0",
66
- "chalk": "^5.4.1",
67
- "check-disk-space": "^3.4.0",
68
- "compression": "^1.8.1",
69
- "cookie-parser": "^1.4.7",
70
- "cronstrue": "^3.2.0",
71
- "csv-parse": "^6.1.0",
72
- "date-fns": "^4.1.0",
73
- "express-csrf-double-submit-cookie": "^2.0.0",
74
- "express-formidable": "^1.2.0",
75
- "express-mongo-sanitize": "^2.2.0",
76
- "express-rate-limit": "^8.0.1",
77
- "express-session": "^1.18.2",
78
- "handlebars": "^4.7.8",
79
- "i18next-browser-languagedetector": "^8.2.0",
80
- "isolated-vm": "^5.0.0",
81
- "juice": "^11.0.3",
82
- "mathjs": "^15.1.0",
83
- "mongodb": "^6.18.0",
84
- "node-cache": "^5.1.2",
85
- "node-schedule": "^2.1.1",
86
- "nodemailer": "^7.0.10",
87
- "openai": "^6.9.1",
88
- "passport": "^0.7.0",
89
- "passport-saml-encrypted": "^0.1.13",
90
- "process": "^0.11.10",
91
- "prop-types": "^15.8.1",
92
- "randomcolor": "^0.6.2",
93
- "react-markdown": "^10.1.0",
94
- "read-excel-file": "^6.0.1",
95
- "request-ip": "^3.3.0",
96
- "safe-regex": "^2.1.1",
97
- "sanitize-html": "^2.17.0",
98
- "sirv": "^3.0.2",
99
- "stripe": "^20.0.0",
100
- "swagger-ui-express": "^5.0.1",
101
- "tar": "^7.5.2",
102
- "tinycolor2": "^1.6.0",
103
- "uniqid": "^5.4.0",
104
- "vitest": "^3.2.4",
105
- "yaml": "^2.8.1"
106
- },
107
- "peerDependencies": {
108
- "express": "^5.1.0",
109
- "passport-azure-ad": "^4.3.5",
110
- "passport-google-oauth20": "^2.0.0",
111
- "react": "18.3.1",
112
- "react-i18next": "^16.3.4",
113
- "react-query": "^3.39.3",
114
- "@langchain/anthropic": "^0.3.33",
115
- "@langchain/deepseek": "^0.1.0",
116
- "@langchain/google-genai": "^0.2.18",
117
- "@langchain/openai": "^0.6.16"
118
- },
119
- "devDependencies": {
120
- "@eslint/js": "^9.32.0",
121
- "concurrently": "^9.2.0",
122
- "cross-env": "^10.0.0",
123
- "eslint": "^9.32.0",
124
- "globals": "^16.3.0"
125
- },
126
- "subPackages": [
127
- "client"
128
- ],
129
- "keywords": [
130
- "data-driven engine",
131
- "headless CMS",
132
- "backend",
133
- "automation",
134
- "AWS S3",
135
- "MongoDB"
136
- ],
137
- "author": "anonympins",
138
- "license": "MIT",
139
- "engines": {
140
- "node": ">=20.0.0"
141
- }
142
- }
1
+ {
2
+ "name": "data-primals-engine",
3
+ "version": "1.6.2",
4
+ "description": "data-primals-engine is a package responsible from handling large amount of data using MongoDB in a practical and performant way. It can also get workflow models working (for automation), and fully supports internationalisation. It also has integrated AI assistant.",
5
+ "main": "src/engine.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "preinstall": "npx force-resolutions",
9
+ "dev": "concurrently \"npm:client\" \"npm:devserver\"",
10
+ "prod": "npm run build-server && npm run server",
11
+ "client": "cd client && npm run dev",
12
+ "build-server": "vite build client --config client/vite.config.js --outDir dist",
13
+ "build-client": "cd client && npm run build",
14
+ "server": "cross-env NODE_ENV=production node server.js",
15
+ "devserver": "npm run build-server && cross-env NODE_ENV=development PORT=7633 node server.js",
16
+ "lint": "eslint .",
17
+ "format": "prettier --write .",
18
+ "clean": "rm -rf node_modules package-lock.json",
19
+ "test": "cross-env MONGO_DB_URL=\"mongodb://localhost:27017\" PORT=7635 vitest --no-file-parallelism --no-watch",
20
+ "audit": "npm audit --audit-level=high",
21
+ "migrate:create": "node src/migrate.js create",
22
+ "migrate:up": "node src/migrate.js up",
23
+ "migrate:down": "node src/migrate.js down",
24
+ "migrate:revert": "node src/migrate.js revert",
25
+ "migrate:status": "node src/migrate.js status"
26
+ },
27
+ "optionalDependencies": {
28
+ "@rollup/rollup-linux-x64-gnu": "4.6.1"
29
+ },
30
+ "resolutions": {
31
+ "on-headers": "1.1.0",
32
+ "brace-expansion": "2.0.2",
33
+ "prismjs": "1.30.0",
34
+ "xml2js": ">=0.5.0",
35
+ "tar-fs": ">=3.1.1",
36
+ "vite": ">=7.0.8",
37
+ "node-forge": ">=1.3.2",
38
+ "js-yaml": ">=4.1.1"
39
+ },
40
+ "overrides": {
41
+ "react-syntax-highlighter": {
42
+ "prismjs": "1.30.0"
43
+ },
44
+ "react-code-blocks": {
45
+ "prismjs": "1.30.0"
46
+ },
47
+ "refractor": {
48
+ "prismjs": "1.30.0"
49
+ }
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/anonympins/data-primals-engine.git"
54
+ },
55
+ "exports": {
56
+ ".": "./src/index.js",
57
+ "./client-only": "./src/client.js",
58
+ "./modules/*.js": "./src/modules/*.js",
59
+ "./modules/*": "./src/modules/*/index.js",
60
+ "./client": "./client/index.js",
61
+ "./*": "./src/*.js"
62
+ },
63
+ "dependencies": {
64
+ "@langchain/core": "^0.3.79",
65
+ "archiver": "^7.0.1",
66
+ "aws-sdk": "^2.1692.0",
67
+ "bcrypt": "^6.0.0",
68
+ "body-parser": "^2.2.1",
69
+ "chalk": "^5.4.1",
70
+ "check-disk-space": "^3.4.0",
71
+ "compression": "^1.8.1",
72
+ "cookie-parser": "^1.4.7",
73
+ "cronstrue": "^3.2.0",
74
+ "csv-parse": "^6.1.0",
75
+ "date-fns": "^4.1.0",
76
+ "express-csrf-double-submit-cookie": "^2.0.0",
77
+ "express-formidable": "^1.2.0",
78
+ "express-mongo-sanitize": "^2.2.0",
79
+ "express-rate-limit": "^8.0.1",
80
+ "express-session": "^1.18.2",
81
+ "handlebars": "^4.7.8",
82
+ "i18next-browser-languagedetector": "^8.2.0",
83
+ "isolated-vm": "^5.0.4",
84
+ "juice": "^11.0.3",
85
+ "mathjs": "^15.1.0",
86
+ "mongodb": "^6.18.0",
87
+ "node-cache": "^5.1.2",
88
+ "node-schedule": "^2.1.1",
89
+ "nodemailer": "^7.0.10",
90
+ "openai": "^6.9.1",
91
+ "passport": "^0.7.0",
92
+ "passport-saml-encrypted": "^0.1.13",
93
+ "process": "^0.11.10",
94
+ "prop-types": "^15.8.1",
95
+ "randomcolor": "^0.6.2",
96
+ "react-markdown": "^10.1.0",
97
+ "read-excel-file": "^6.0.1",
98
+ "request-ip": "^3.3.0",
99
+ "safe-regex": "^2.1.1",
100
+ "sanitize-html": "^2.17.0",
101
+ "sirv": "^3.0.2",
102
+ "stripe": "^20.0.0",
103
+ "swagger-ui-express": "^5.0.1",
104
+ "tar": "^7.5.2",
105
+ "tinycolor2": "^1.6.0",
106
+ "uniqid": "^5.4.0",
107
+ "vitest": "^3.2.4",
108
+ "yaml": "^2.8.1"
109
+ },
110
+ "peerDependencies": {
111
+ "express": "^5.1.0",
112
+ "passport-azure-ad": "^4.3.5",
113
+ "passport-google-oauth20": "^2.0.0",
114
+ "react": "18.3.1",
115
+ "react-i18next": "^16.3.4",
116
+ "react-query": "^3.39.3",
117
+ "@langchain/anthropic": "^0.3.33",
118
+ "@langchain/deepseek": "^0.1.0",
119
+ "@langchain/google-genai": "^0.2.18",
120
+ "@langchain/openai": "^0.6.16"
121
+ },
122
+ "devDependencies": {
123
+ "@eslint/js": "^9.32.0",
124
+ "concurrently": "^9.2.0",
125
+ "cross-env": "^10.0.0",
126
+ "eslint": "^9.32.0",
127
+ "globals": "^16.3.0"
128
+ },
129
+ "subPackages": [
130
+ "client"
131
+ ],
132
+ "keywords": [
133
+ "data-driven engine",
134
+ "headless CMS",
135
+ "backend",
136
+ "automation",
137
+ "AWS S3",
138
+ "MongoDB"
139
+ ],
140
+ "author": "anonympins",
141
+ "license": "MIT",
142
+ "engines": {
143
+ "node": ">=20.0.0"
144
+ }
145
+ }
package/src/client.js ADDED
@@ -0,0 +1,4 @@
1
+ // data-primals-engine/client
2
+ // Ce fichier sert de point d'entrée pour les modules qui peuvent être utilisés en toute sécurité côté client (navigateur).
3
+
4
+ export { Event } from './events.js';
@@ -1,7 +1,8 @@
1
1
  import { getCollectionForUser, modelsCollection } from "../mongodb.js";
2
2
  import { Logger } from "../../gameObject.js";
3
3
  import { HumanMessage, SystemMessage } from "@langchain/core/messages";
4
- import {searchData, patchData, deleteData, insertData} from "../data/index.js";
4
+ import { searchData, patchData, deleteData, insertData } from "../data/index.js";
5
+ import { getAIProvider } from "./providers.js";
5
6
  import {providers} from "./constants.js";
6
7
  import {Config} from "../../config.js";
7
8
  import { Event } from "../../events.js";
@@ -13,38 +14,6 @@ import {parseSafeJSON} from "../../core.js";
13
14
 
14
15
  let logger = null;
15
16
 
16
- export const getAIProvider= async (aiProvider, aiModel, apiKey)=>{
17
- const maxTokens = Config.Get('assistant.maxTokens', assistantConfig.maxTokens);
18
- try {
19
- switch (aiProvider) {
20
- case 'OpenAI': {
21
- const { ChatOpenAI } = await import("@langchain/openai");
22
- return new ChatOpenAI({apiKey, model: aiModel, temperature: 0.7, maxTokens});
23
- }
24
- case 'Google': {
25
- const { ChatGoogleGenerativeAI } = await import("@langchain/google-genai");
26
- return new ChatGoogleGenerativeAI({apiKey, model: aiModel, temperature: 0.7, maxTokens});
27
- }
28
- case 'DeepSeek': {
29
- const { ChatDeepSeek } = await import("@langchain/deepseek");
30
- return new ChatDeepSeek({apiKey, model: aiModel, temperature: 0.7, maxTokens});
31
- }
32
- case 'Anthropic': {
33
- const { ChatAnthropic } = await import("@langchain/anthropic");
34
- return new ChatAnthropic({apiKey, model: aiModel, temperature: 0.7, maxTokens});
35
- }
36
- default:
37
- throw new Error(`Unsupported AI provider: ${aiProvider}`);
38
- }
39
- } catch (e) {
40
- if (e.code === 'ERR_MODULE_NOT_FOUND') {
41
- logger.error(`[Assistant] The package for the '${aiProvider}' provider is not installed. Please run 'npm install @langchain/${aiProvider.toLowerCase()}' to use this provider.`);
42
- throw new Error(`The AI provider '${aiProvider}' is not installed. Please ask the administrator to install the corresponding package.`);
43
- }
44
- logger.error(`[Assistant] Error initializing AI provider '${aiProvider}': ${e.message}`);
45
- throw e; // Re-throw other errors
46
- }
47
- }
48
17
  export const assistantGlobalLimiter = rateLimit({
49
18
  windowMs: 15 * 60 * 1000, // Fenêtre de 15 minutes
50
19
  max: 100, // Limite à 100 requêtes globales pour l'assistant pendant la fenêtre (vous pouvez ajuster cette valeur)
@@ -101,7 +70,7 @@ const createSystemPrompt = (modelDefs, lang) => {
101
70
 
102
71
  const date = new Date();
103
72
  const dt = date.toISOString();
104
- return `
73
+ return Config.Get('systemPrompt', `
105
74
  Tu es "Prior", un assistant expert en analyse de données pour le moteur data-primals-engine..
106
75
  Ta mission est d'aider l'utilisateur en répondant à ses questions sur ses données.
107
76
 
@@ -333,7 +302,7 @@ Ma question: Bonjour, je voudrais les requêtes effectuées aujourd'hui sur le m
333
302
  Ta réponse: [
334
303
  { "action" : "search_models", "params": { "query": "request" } },
335
304
  { "action" : "search", "params" : { "model": "request", "filter": { "$and": [{"$gte": ["$createdAt", "${dt}"]}, {"$regexMatch": { "input": "$url", "regex": "content"}}] }, "limit" : 10, "sort" : "_id:DESC" } }
336
- ]`;
305
+ ]`);
337
306
  }
338
307
 
339
308
  /**
@@ -475,7 +444,7 @@ export async function handleChatRequest(params, user, sendEvent = null) {
475
444
  return errorResult;
476
445
  }
477
446
 
478
- const systemPrompt = await Event.Trigger('OnSystemPrompt', 'event', 'user', user);
447
+ const systemPrompt = (await Event.Trigger('OnSystemPrompt', 'event', 'user', user)) || createSystemPrompt([], user.lang || 'en');
479
448
 
480
449
  const conversationHistory = (history || [])
481
450
  .filter(msg => msg.text && !(msg.from === 'bot' && msg.text.startsWith(i18n.t('assistant.welcome'))))
@@ -559,11 +528,12 @@ export async function handleChatRequest(params, user, sendEvent = null) {
559
528
  return result;
560
529
  }
561
530
 
562
- let hasContinued = false;
531
+ const toolResults = [];
532
+ let finalActionResult = null;
533
+
563
534
  for (const command of commands) {
564
535
  logger.debug(`[Assistant] Action décidée par l'IA: ${command.action}`, command);
565
536
  if (sendEvent) sendEvent('action', { action: command.action, params: command.params });
566
- conversationHistory.push(new SystemMessage(JSON.stringify(command)));
567
537
 
568
538
  const { action, params: parsedParams } = command;
569
539
 
@@ -576,40 +546,52 @@ export async function handleChatRequest(params, user, sendEvent = null) {
576
546
  // Outils pour le raisonnement interne de l'IA
577
547
  if (['search_models'].includes(action)) {
578
548
  const toolResult = await executeTool(action, parsedParams, user, allModels);
579
- if (sendEvent) sendEvent('tool_result', { action, result: toolResult });
580
- conversationHistory.push(new SystemMessage(`Résultat de l'outil '${action}':\n${toolResult}`));
581
- hasContinued = true; // On indique qu'on doit continuer la boucle principale
582
- continue; // On passe à la commande suivante dans la liste de l'IA
549
+ toolResults.push({ action, result: toolResult });
550
+ continue; // Passe à la commande suivante sans vérifier si c'est une action finale
583
551
  }
584
552
 
585
553
  // Actions finales
586
- const res = await Event.Trigger('OnChatAction', 'event', 'user', action, params, command, llmOptions, user);
554
+ const res = await Event.Trigger('OnChatAction', 'event', 'user', action, parsedParams, command, llmOptions, user, params);
587
555
 
588
- if (!res) {
556
+ if (res) {
557
+ // On a trouvé une action finale. On la stocke et on arrête de chercher.
558
+ finalActionResult = res;
559
+ break; // Sort de la boucle des commandes
560
+ } else {
589
561
  // Si l'action n'est reconnue par aucune des logiques ci-dessus
590
562
  logger.warn(`[Assistant] Action non reconnue reçue de l'IA: ${action}`);
591
- const result = {
563
+ finalActionResult = {
592
564
  success: true,
593
565
  displayMessage: i18n.t('assistant.unknownAction', "Désolé, je ne comprends pas la commande '{{action}}'.", { action })
594
566
  };
595
- if (sendEvent) {
596
- sendEvent('final_result', result);
597
- return;
598
- }
599
- return result;
567
+ break; // Sortir de la boucle des commandes
600
568
  }
601
- // Une action finale a été trouvée, on retourne son résultat et on arrête le traitement.
569
+ }
570
+
571
+ // Si une action finale a été exécutée, on retourne son résultat.
572
+ if (finalActionResult) {
602
573
  if (sendEvent) {
603
- sendEvent('final_result', res);
574
+ sendEvent('final_result', finalActionResult);
604
575
  return;
605
576
  }
606
- return res;
577
+ return finalActionResult;
607
578
  }
608
579
 
609
- // Si on a traité des outils de raisonnement, on relance la boucle de l'IA
610
- if (hasContinued) {
580
+ // Si on a uniquement des résultats d'outils, on les ajoute à l'historique et on continue la boucle.
581
+ if (toolResults.length > 0) {
582
+ let toolResponse = "";
583
+ for (const tool of toolResults) {
584
+ if (sendEvent) sendEvent('tool_result', { action: tool.action, result: tool.result });
585
+ toolResponse += `Résultat de l'outil '${tool.action}':\n${tool.result}\n\n`;
586
+ }
587
+ conversationHistory.push(new SystemMessage(toolResponse));
611
588
  continue;
612
589
  }
590
+
591
+ // Si l'IA ne retourne ni outil ni action finale reconnue, on arrête.
592
+ if (commands.length > 0) {
593
+ logger.warn(`[Assistant] L'IA a retourné des commandes mais aucune n'était un outil ou une action finale reconnue. Commandes:`, commands);
594
+ }
613
595
  }
614
596
 
615
597
  // Si la boucle se termine sans une action finale
@@ -656,7 +638,7 @@ async function executeConfirmedAction(action, params, user) {
656
638
  * @param {object} user - L'objet utilisateur.
657
639
  * @returns {Promise<object|boolean>} - L'objet de réponse final ou false si l'action n'est pas reconnue.
658
640
  */
659
- async function handleFinalChatAction(action, params, parsedResponse, user) {
641
+ async function handleFinalChatAction(action, params, parsedResponse, llmOptions, user) {
660
642
  // Action de génération de graphique, gérée par le front-end
661
643
  if (action === 'generateChart') {
662
644
  return {success: true, chartConfig: params};
@@ -0,0 +1,38 @@
1
+ import { Config } from "../../config.js";
2
+ import { assistantConfig } from "../../constants.js";
3
+ import { Logger } from "../../gameObject.js";
4
+
5
+ const logger = new Logger("AssistantProviders");
6
+
7
+ export const getAIProvider = async (aiProvider, aiModel, apiKey) => {
8
+ const maxTokens = Config.Get('assistant.maxTokens', assistantConfig.maxTokens);
9
+ try {
10
+ switch (aiProvider) {
11
+ case 'OpenAI': {
12
+ const { ChatOpenAI } = await import("@langchain/openai");
13
+ return new ChatOpenAI({ apiKey, model: aiModel, temperature: 0.7, maxTokens });
14
+ }
15
+ case 'Google': {
16
+ const { ChatGoogleGenerativeAI } = await import("@langchain/google-genai");
17
+ return new ChatGoogleGenerativeAI({ apiKey, model: aiModel, temperature: 0.7, maxTokens });
18
+ }
19
+ case 'DeepSeek': {
20
+ const { ChatDeepSeek } = await import("@langchain/deepseek");
21
+ return new ChatDeepSeek({ apiKey, model: aiModel, temperature: 0.7, maxTokens });
22
+ }
23
+ case 'Anthropic': {
24
+ const { ChatAnthropic } = await import("@langchain/anthropic");
25
+ return new ChatAnthropic({ apiKey, model: aiModel, temperature: 0.7, maxTokens });
26
+ }
27
+ default:
28
+ throw new Error(`Unsupported AI provider: ${aiProvider}`);
29
+ }
30
+ } catch (e) {
31
+ if (e.code === 'ERR_MODULE_NOT_FOUND') {
32
+ logger.error(`[Assistant] The package for the '${aiProvider}' provider is not installed. Please run 'npm install @langchain/${aiProvider.toLowerCase()}' to use this provider.`);
33
+ throw new Error(`The AI provider '${aiProvider}' is not installed. Please ask the administrator to install the corresponding package.`);
34
+ }
35
+ logger.error(`[Assistant] Error initializing AI provider '${aiProvider}': ${e.message}`);
36
+ throw e; // Re-throw other errors
37
+ }
38
+ }
@@ -323,17 +323,14 @@ export function onInit(defaultEngine) {
323
323
  engine.post('/api/data/history/:modelName/:recordId/revert/:version', [middlewareAuthenticator, userInitiator], handleRevertToRevisionRequest);
324
324
 
325
325
  // --- Écouteur pour la CRÉATION de données (Version 1 - Snapshot) ---
326
- Event.Listen("OnDataAdded", async (engine, { modelName, insertedIds, user }) => {
326
+ Event.Listen("OnDataAdded", async (engine, { modelName, insertedDocs, user }) => {
327
327
  try {
328
328
  const model = await getModel(modelName, user);
329
329
  if (!model?.history?.enabled) return;
330
330
 
331
- const dataCollection = await getCollectionForUser(user);
332
331
  const historyCollection = getCollection('history');
333
332
 
334
- const newDocs = await dataCollection.find({ _id: { $in: insertedIds.map(id => new ObjectId(id)) } }).toArray();
335
-
336
- for (const doc of newDocs) {
333
+ for (const doc of insertedDocs) {
337
334
  await historyCollection.insertOne({
338
335
  documentId: doc._id,
339
336
  model: modelName,
@@ -817,7 +817,7 @@ export const insertData = async (modelName, data, files, user, triggerWorkflow =
817
817
  }
818
818
 
819
819
  // System specific event
820
- const eventPayload = {modelName, insertedIds, user};
820
+ const eventPayload = {modelName, insertedDocs, user};
821
821
  await Event.Trigger("OnDataAdded", "event", "system", engine, eventPayload);
822
822
 
823
823
  // User specific event
@@ -829,7 +829,10 @@ export const insertData = async (modelName, data, files, user, triggerWorkflow =
829
829
  // --- CORRECTION ---
830
830
  // On renvoie l'objet complet du premier document inséré (cas le plus courant pour l'undo)
831
831
  // et la liste complète des IDs pour les cas de bulk insert.
832
- const firstInsertedDoc = insertedDocs.length > 0 ? insertedDocs[0] : null;
832
+ let firstInsertedDoc = insertedDocs.length > 0 ? { ...insertedDocs[0] } : null;
833
+ if (firstInsertedDoc?._id) {
834
+ firstInsertedDoc._id = firstInsertedDoc._id.toString();
835
+ }
833
836
  return {success: true, data: firstInsertedDoc, insertedIds: insertedIds.map(id => id.toString())};
834
837
 
835
838
  } catch (error) { // Attrape les erreurs de permission ou de pushDataUnsecure
@@ -1419,6 +1422,12 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
1419
1422
  before: existingDocs, // Documents avant la modification
1420
1423
  after: updatedDocs // Documents après la modification
1421
1424
  });
1425
+ await Event.Trigger("OnDataEdited", "event", "user", engine, {
1426
+ modelName,
1427
+ user,
1428
+ before: existingDocs, // Documents avant la modification
1429
+ after: updatedDocs // Documents après la modification
1430
+ });
1422
1431
  }
1423
1432
 
1424
1433
  // 11. Tâches post-mise à jour (schedules, workflows) (inchangé)
@@ -17,8 +17,8 @@ import {isConditionMet} from "../filter.js";
17
17
  import { services } from '../services/index.js';
18
18
  import {getEnv, getSmtpConfig} from "./user.js";
19
19
  import {getHost} from "../constants.js";
20
- import {providers} from "./assistant/constants.js";
21
- import {getAIProvider} from "./assistant/assistant.js";
20
+ import { providers } from "./assistant/constants.js";
21
+ import { getAIProvider } from "./assistant/providers.js";
22
22
  import {parseSafeJSON, safeAssignObject} from "../core.js";
23
23
  import {Config} from "../config.js";
24
24