data-primals-engine 1.7.1 → 1.7.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/README.md +160 -160
- package/client/package-lock.json +484 -175
- package/client/package.json +7 -4
- package/client/src/AssistantChat.jsx +1 -3
- package/client/src/DataLayout.jsx +19 -20
- package/client/src/DataTable.jsx +2 -2
- package/client/src/DocumentationPageLayout.scss +1 -1
- package/client/src/ViewSwitcher.jsx +1 -1
- package/client/vite.config.js +31 -30
- package/package.json +27 -17
- package/src/ai.jobs.js +135 -0
- package/src/constants.js +561 -545
- package/src/data.js +2 -0
- package/src/engine.js +50 -42
- package/src/modules/assistant/assistant.js +782 -763
- package/src/modules/assistant/constants.js +23 -16
- package/src/modules/assistant/providers.js +77 -37
- package/src/modules/bucket.js +4 -0
- package/src/modules/data/data.cluster.js +191 -0
- package/src/modules/data/data.core.js +11 -8
- package/src/modules/data/data.js +311 -311
- package/src/modules/data/data.operations.js +186 -106
- package/src/modules/data/data.relations.js +1 -0
- package/src/modules/data/data.replication.js +83 -0
- package/src/modules/data/data.routes.js +2183 -1879
- package/src/modules/mongodb.js +76 -73
- package/src/modules/user.js +7 -1
- package/src/modules/worker-script-runner.js +97 -0
- package/src/modules/workflow.js +177 -52
- package/src/packs.js +5701 -5701
- package/src/providers.js +298 -297
- package/test/assistant.test.js +207 -206
- package/test/data.integration.test.js +1425 -1416
- package/test/import_export.integration.test.js +210 -210
- package/test/workflow.actions.integration.test.js +487 -475
- package/test/workflow.integration.test.js +332 -329
package/client/package.json
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"preview": "vite preview"
|
|
14
14
|
},
|
|
15
15
|
"resolutions": {
|
|
16
|
+
"markdown-it": ">=14.1.1",
|
|
16
17
|
"immutable": ">=5.1.5",
|
|
17
18
|
"body-parser": ">=2.2.1",
|
|
18
19
|
"js-yaml": ">=4.1.1",
|
|
@@ -31,7 +32,6 @@
|
|
|
31
32
|
"react-cookie": "^8.0.1",
|
|
32
33
|
"react-ga": "^3.3.1",
|
|
33
34
|
"react-helmet": "^6.1.0",
|
|
34
|
-
"react-icons": "^5.5.0",
|
|
35
35
|
"react-international-phone": "^4.6.0",
|
|
36
36
|
"react-query": "^3.39.3",
|
|
37
37
|
"react-router-dom": "^7.8.1",
|
|
@@ -63,14 +63,17 @@
|
|
|
63
63
|
"lowlight": "^3.3.0",
|
|
64
64
|
"react": "^18.3.1",
|
|
65
65
|
"reactflow": "^11.0.0-next.0",
|
|
66
|
+
"react-icons": "^5.5.0",
|
|
66
67
|
"react-big-calendar": "^1.15.0",
|
|
67
68
|
"react-color": "^2.19.3",
|
|
68
69
|
"react-dom": "^18.3.1",
|
|
69
70
|
"react-leaflet": "^4.2.1",
|
|
70
|
-
"react-router": "7.
|
|
71
|
+
"react-router": ">=7.15.1",
|
|
71
72
|
"react-switch": "^7.1.0",
|
|
72
73
|
"uniqid": "^5.4.0",
|
|
73
|
-
"yet-another-react-lightbox": "^3.25.0"
|
|
74
|
+
"yet-another-react-lightbox": "^3.25.0",
|
|
75
|
+
"flatted": ">=3.4.2",
|
|
76
|
+
"linkify-it": ">=5.0.1"
|
|
74
77
|
},
|
|
75
78
|
"devDependencies": {
|
|
76
79
|
"@eslint/js": "^9.33.0",
|
|
@@ -84,6 +87,6 @@
|
|
|
84
87
|
"globals": "^15.15.0",
|
|
85
88
|
"process": "^0.11.10",
|
|
86
89
|
"sass-embedded": "^1.90.0",
|
|
87
|
-
"vite": "
|
|
90
|
+
"vite": ">=6.4.3"
|
|
88
91
|
}
|
|
89
92
|
}
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// client/src/components/AssistantChat.jsx
|
|
2
|
-
|
|
3
1
|
import React, { useState, useMemo, useEffect, useRef } from 'react';
|
|
4
2
|
import { FaRobot, FaPaperPlane, FaTimes, FaExpand, FaCompress, FaPlus } from 'react-icons/fa';
|
|
5
3
|
import './AssistantChat.scss';
|
|
@@ -13,7 +11,7 @@ import FlexViewCard from "./FlexViewCard.jsx";
|
|
|
13
11
|
import HtmlViewCard from "./HtmlViewCard.jsx";
|
|
14
12
|
import {useUI} from "./contexts/UIContext.jsx";
|
|
15
13
|
import Button from "./Button.jsx";
|
|
16
|
-
import {getUserHash
|
|
14
|
+
import {getUserHash} from "../../src/data.js";
|
|
17
15
|
import {useAuthContext} from "./contexts/AuthContext.jsx";
|
|
18
16
|
import {useNavigate} from "react-router-dom";
|
|
19
17
|
import {DataTable} from "./DataTable.jsx";
|
|
@@ -60,7 +60,6 @@ const NotConfiguredPlaceholder = ({ type, onConfigure }) => (
|
|
|
60
60
|
|
|
61
61
|
const WorkflowSelectorModal = ({ onClose, onSelectWorkflow }) => {
|
|
62
62
|
const { t } = useTranslation();
|
|
63
|
-
const { searchData } = useModelContext();
|
|
64
63
|
const { me } = useAuthContext();
|
|
65
64
|
const { data: activeWorkflows, isLoading } = useQuery(
|
|
66
65
|
'activeWorkflowsList',
|
|
@@ -96,7 +95,7 @@ const WorkflowSelectorModal = ({ onClose, onSelectWorkflow }) => {
|
|
|
96
95
|
);
|
|
97
96
|
};
|
|
98
97
|
|
|
99
|
-
function DataLayout({refreshUI}) {
|
|
98
|
+
function DataLayout({refreshUI}, ref) {
|
|
100
99
|
const [ searchParams, setSearchParams ] = useSearchParams();
|
|
101
100
|
const [viewSettings, setViewSettings] = useLocalStorage('viewSettings', {});
|
|
102
101
|
|
|
@@ -147,6 +146,7 @@ function DataLayout({refreshUI}) {
|
|
|
147
146
|
|
|
148
147
|
const mainPartRef = useRef();
|
|
149
148
|
const modelCreatorRef = useRef();
|
|
149
|
+
const dataEditorRef = useRef(null);
|
|
150
150
|
|
|
151
151
|
const [tutorialDialogVisible, setTutorialDialogVisible] = useState(false);
|
|
152
152
|
const [importModalVisible, setImportModalVisible] = useState(false);
|
|
@@ -155,7 +155,6 @@ function DataLayout({refreshUI}) {
|
|
|
155
155
|
const [showAPIInfo, setAPIInfoVisible] = useState(false);
|
|
156
156
|
const nav = useNavigate();
|
|
157
157
|
const mod = searchParams.get('model');
|
|
158
|
-
const loc = useLocation();
|
|
159
158
|
|
|
160
159
|
|
|
161
160
|
// --- AJOUT : Récupérer le paramètre de l'URL pour l'édition de workflow ---
|
|
@@ -300,18 +299,24 @@ function DataLayout({refreshUI}) {
|
|
|
300
299
|
setFilterValues={setFilterValues}
|
|
301
300
|
model={selectedModel}
|
|
302
301
|
onAddData={(model) => {
|
|
303
|
-
mainPartRef.current.scrollIntoView({behavior: "smooth"});
|
|
304
302
|
handleAddData(model);
|
|
303
|
+
setTimeout(() => {
|
|
304
|
+
dataEditorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
305
|
+
}, 100);
|
|
305
306
|
}}
|
|
306
307
|
onDuplicateData={(data) => {
|
|
307
|
-
mainPartRef.current.scrollIntoView({behavior: "smooth"});
|
|
308
308
|
handleAddData(selectedModel, data);
|
|
309
|
+
setTimeout(() => {
|
|
310
|
+
dataEditorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
311
|
+
}, 100);
|
|
309
312
|
}}
|
|
310
313
|
onEdit={(item) => {
|
|
311
|
-
mainPartRef.current.scrollIntoView({behavior: "smooth"});
|
|
312
314
|
setRecordToEdit(item);
|
|
313
315
|
setFormData(item);
|
|
314
316
|
setDataEditorVisible(true);
|
|
317
|
+
setTimeout(() => {
|
|
318
|
+
dataEditorRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
319
|
+
}, 100);
|
|
315
320
|
}}
|
|
316
321
|
deleteApiCall={deleteApiCall}
|
|
317
322
|
queryClient={queryClient}
|
|
@@ -586,13 +591,6 @@ function DataLayout({refreshUI}) {
|
|
|
586
591
|
setImportModalVisible(false);
|
|
587
592
|
}
|
|
588
593
|
|
|
589
|
-
useEffect(() => {
|
|
590
|
-
if( showDataEditor && mainPartRef.current ){
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
}, [showDataEditor,mainPartRef.current]);
|
|
594
|
-
|
|
595
|
-
|
|
596
594
|
const [currentProfile, setCurrentProfile] = useLocalStorage('profile', null);
|
|
597
595
|
const {isTourOpen, setIsTourOpen, currentTourSteps, allTourSteps, setTourStepIndex, setCurrentTourSteps, currentTour,setCurrentTour, addLaunchedTour, assistantConfig, setAssistantConfig} = useUI();
|
|
598
596
|
|
|
@@ -707,7 +705,7 @@ function DataLayout({refreshUI}) {
|
|
|
707
705
|
onSelectWorkflow={(id) => {
|
|
708
706
|
nav(`?edit-workflow=${id}`);
|
|
709
707
|
setEditionMode(false)
|
|
710
|
-
mainPartRef.current
|
|
708
|
+
mainPartRef.current?.scrollIntoView();
|
|
711
709
|
setCurrentView('workflow'); // Ajout pour forcer le changement de vue
|
|
712
710
|
setWorkflowListModalOpen(false);
|
|
713
711
|
}}
|
|
@@ -740,7 +738,7 @@ function DataLayout({refreshUI}) {
|
|
|
740
738
|
setAPIInfoVisible(false);
|
|
741
739
|
setDataEditorVisible(false);
|
|
742
740
|
setEditionMode(true);
|
|
743
|
-
mainPartRef.current
|
|
741
|
+
mainPartRef.current?.scrollIntoView({behavior: 'smooth'});
|
|
744
742
|
gtag("event", "select_content", {
|
|
745
743
|
content_type: "create_model",
|
|
746
744
|
});
|
|
@@ -750,7 +748,7 @@ function DataLayout({refreshUI}) {
|
|
|
750
748
|
setAPIInfoVisible(false);
|
|
751
749
|
setEditionMode(true);
|
|
752
750
|
|
|
753
|
-
mainPartRef.current
|
|
751
|
+
mainPartRef.current?.scrollIntoView({behavior: 'smooth'});
|
|
754
752
|
gtag("event", "select_content", {
|
|
755
753
|
content_type: "edit_model",
|
|
756
754
|
content_id: model.name
|
|
@@ -760,7 +758,7 @@ function DataLayout({refreshUI}) {
|
|
|
760
758
|
setDataEditorVisible(false);
|
|
761
759
|
setEditionMode(false);
|
|
762
760
|
setAPIInfoVisible(false);
|
|
763
|
-
mainPartRef.current
|
|
761
|
+
//mainPartRef.current?.scrollIntoView({behavior: 'smooth'});
|
|
764
762
|
gtag("event", "select_content", {
|
|
765
763
|
content_type: "select_model",
|
|
766
764
|
content_id: model.name
|
|
@@ -768,12 +766,12 @@ function DataLayout({refreshUI}) {
|
|
|
768
766
|
}} onImportPack={() => {
|
|
769
767
|
setShowPackGallery(true);
|
|
770
768
|
}} onNewData={(model) => {
|
|
771
|
-
mainPartRef.current
|
|
769
|
+
mainPartRef.current?.scrollIntoView({behavior: 'smooth'});
|
|
772
770
|
handleAddData(model);
|
|
773
771
|
}}/>
|
|
774
772
|
{(editionMode) && (
|
|
775
773
|
<ModelCreator ref={modelCreatorRef} onModelGenerated={() =>{
|
|
776
|
-
modelCreatorRef
|
|
774
|
+
modelCreatorRef?.current?.scrollIntoView({behavior: 'smooth'});
|
|
777
775
|
}} initialModel={selectedModel} onModelSaved={(model) => {
|
|
778
776
|
setRefreshTime(new Date().getTime());
|
|
779
777
|
handleModelSelect(model);
|
|
@@ -785,7 +783,8 @@ function DataLayout({refreshUI}) {
|
|
|
785
783
|
)}
|
|
786
784
|
<div className="hidden-anchor" ref={mainPartRef}></div>
|
|
787
785
|
|
|
788
|
-
{showDataEditor && (<DataEditor
|
|
786
|
+
{showDataEditor && (<DataEditor
|
|
787
|
+
ref={dataEditorRef}
|
|
789
788
|
key={selectedModel?.name}
|
|
790
789
|
isLoading={isLoading}
|
|
791
790
|
model={selectedModel}
|
package/client/src/DataTable.jsx
CHANGED
|
@@ -738,10 +738,10 @@ export function DataTable({
|
|
|
738
738
|
</button>)}
|
|
739
739
|
{/* AJOUT : Bouton pour ouvrir l'éditeur de workflow */}
|
|
740
740
|
{model.name === 'workflow' && (
|
|
741
|
-
<
|
|
741
|
+
<button onClick={() => nav(`?edit-workflow=${item._id}`)} className="btn-nav datatable-action-btn workflow-edit"
|
|
742
742
|
data-tooltip-id="tooltipActions" data-tooltip-content={t('workflow.editor.title', "Éditeur de Workflow")}>
|
|
743
743
|
<FaGear />
|
|
744
|
-
</
|
|
744
|
+
</button>
|
|
745
745
|
)}
|
|
746
746
|
|
|
747
747
|
<button data-tooltip-id="tooltipActions"
|
|
@@ -17,7 +17,7 @@ const ViewSwitcher = ({ currentView, onViewChange, configuredViews, onConfigureV
|
|
|
17
17
|
const { t } = useTranslation();
|
|
18
18
|
|
|
19
19
|
return (
|
|
20
|
-
<div className="view-switcher flex items-center gap-1 p-1
|
|
20
|
+
<div className="view-switcher flex items-center gap-1 p-1 rounded-md">
|
|
21
21
|
{viewOptions.map(view => {
|
|
22
22
|
const isConfigured = view.id === 'table' || configuredViews[view.id];
|
|
23
23
|
const isActive = currentView === view.id;
|
package/client/vite.config.js
CHANGED
|
@@ -1,30 +1,31 @@
|
|
|
1
|
-
import { defineConfig } from 'vite'
|
|
2
|
-
import react from '@vitejs/plugin-react'
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
//
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
}
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import react from '@vitejs/plugin-react'
|
|
3
|
+
import { fileURLToPath, URL } from "node:url";
|
|
4
|
+
|
|
5
|
+
// https://vite.dev/config/
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [react()],
|
|
8
|
+
base: "/",
|
|
9
|
+
build: {
|
|
10
|
+
outDir: "dist"
|
|
11
|
+
},
|
|
12
|
+
server : {
|
|
13
|
+
proxy: {
|
|
14
|
+
'/api': {
|
|
15
|
+
target: 'http://localhost:7633',
|
|
16
|
+
secure: false,
|
|
17
|
+
changeOrigin: true,
|
|
18
|
+
// Optionnel mais utile pour le débogage :
|
|
19
|
+
// Affiche les requêtes proxy dans la console de Vite.
|
|
20
|
+
configure: (proxy, options) => {
|
|
21
|
+
proxy.on('proxyReq', (proxyReq, req, res) => {
|
|
22
|
+
console.log(`[Vite Proxy] Forwarding request: ${req.method} ${req.url} -> ${options.target}${proxyReq.path}`);
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
fs: {
|
|
29
|
+
allow: ["./src", "../src"]
|
|
30
|
+
}
|
|
31
|
+
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "data-primals-engine",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.2",
|
|
4
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
5
|
"main": "src/engine.js",
|
|
6
6
|
"type": "module",
|
|
@@ -28,10 +28,14 @@
|
|
|
28
28
|
"@rollup/rollup-linux-x64-gnu": "4.6.1"
|
|
29
29
|
},
|
|
30
30
|
"resolutions": {
|
|
31
|
+
"shell-quote": ">=1.8.4",
|
|
32
|
+
"lodash": ">=4.18.0",
|
|
31
33
|
"flatted": ">=3.4.2",
|
|
32
34
|
"rollup": ">=4.59.0",
|
|
33
35
|
"on-headers": ">=1.1.0",
|
|
34
|
-
"brace-expansion": ">=2.0.
|
|
36
|
+
"brace-expansion": ">=2.0.3",
|
|
37
|
+
"minimatch": ">=3.1.4",
|
|
38
|
+
"path-to-regexp": ">=8.4.0",
|
|
35
39
|
"prismjs": ">=1.30.0",
|
|
36
40
|
"xml2js": ">=0.5.0",
|
|
37
41
|
"tar-fs": ">=3.1.1",
|
|
@@ -39,13 +43,17 @@
|
|
|
39
43
|
"node-forge": ">=1.4.0",
|
|
40
44
|
"js-yaml": ">=4.1.1",
|
|
41
45
|
"mdast-util-to-hast": ">=13.2.1",
|
|
46
|
+
"uuid": ">=11.1.1",
|
|
42
47
|
"jws": ">=3.2.3",
|
|
43
|
-
"undici": ">=
|
|
48
|
+
"undici": ">=7.28.0",
|
|
44
49
|
"@xmldom/xmldom": ">=0.8.12",
|
|
45
50
|
"picomatch": ">=4.0.4",
|
|
46
|
-
"langsmith": ">=0.
|
|
51
|
+
"langsmith": ">=0.6.0",
|
|
47
52
|
"qs": ">=6.14.2",
|
|
48
|
-
"fast-xml-parser": ">=4.5.5"
|
|
53
|
+
"fast-xml-parser": ">=4.5.5",
|
|
54
|
+
"fast-xml-builder": ">=1.1.7",
|
|
55
|
+
"ip-address": ">=10.1.1",
|
|
56
|
+
"postcss": ">=8.5.10"
|
|
49
57
|
},
|
|
50
58
|
"overrides": {
|
|
51
59
|
"react-syntax-highlighter": {
|
|
@@ -78,7 +86,7 @@
|
|
|
78
86
|
},
|
|
79
87
|
"dependencies": {
|
|
80
88
|
"@langchain/core": "^0.3.80",
|
|
81
|
-
"archiver": "^
|
|
89
|
+
"archiver": "^8.0.0",
|
|
82
90
|
"aws-sdk": "^2.1692.0",
|
|
83
91
|
"bcrypt": "^6.0.0",
|
|
84
92
|
"body-parser": ">=2.2.1",
|
|
@@ -89,6 +97,7 @@
|
|
|
89
97
|
"cronstrue": "^3.2.0",
|
|
90
98
|
"csv-parse": "^6.1.0",
|
|
91
99
|
"date-fns": "^4.1.0",
|
|
100
|
+
"dotenv": "^17.4.2",
|
|
92
101
|
"express-csrf-double-submit-cookie": "^2.0.0",
|
|
93
102
|
"express-formidable": "^1.2.0",
|
|
94
103
|
"express-mongo-sanitize": "^2.2.0",
|
|
@@ -98,11 +107,12 @@
|
|
|
98
107
|
"i18next-browser-languagedetector": "^8.2.0",
|
|
99
108
|
"isolated-vm": "^5.0.4",
|
|
100
109
|
"juice": "^11.0.3",
|
|
110
|
+
"leaflet": "^1.9.4",
|
|
101
111
|
"mathjs": "15.2.0",
|
|
102
112
|
"mongodb": "^6.18.0",
|
|
103
113
|
"node-cache": "^5.1.2",
|
|
104
114
|
"node-schedule": "^2.1.1",
|
|
105
|
-
"nodemailer": "
|
|
115
|
+
"nodemailer": ">=9.0.1",
|
|
106
116
|
"openai": "^6.9.1",
|
|
107
117
|
"passport": "^0.7.0",
|
|
108
118
|
"passport-saml-encrypted": "^0.1.13",
|
|
@@ -113,33 +123,33 @@
|
|
|
113
123
|
"read-excel-file": "^6.0.1",
|
|
114
124
|
"request-ip": "^3.3.0",
|
|
115
125
|
"safe-regex": "^2.1.1",
|
|
116
|
-
"sanitize-html": "^2.17.
|
|
126
|
+
"sanitize-html": "^2.17.4",
|
|
117
127
|
"sirv": "^3.0.2",
|
|
118
128
|
"stripe": "^20.0.0",
|
|
119
129
|
"swagger-ui-express": "^5.0.1",
|
|
120
|
-
"tar": "7.5.
|
|
130
|
+
"tar": ">=7.5.16",
|
|
121
131
|
"tinycolor2": "^1.6.0",
|
|
122
132
|
"uniqid": "^5.4.0",
|
|
123
|
-
"vitest": "3.2.
|
|
133
|
+
"vitest": "3.2.6",
|
|
124
134
|
"yaml": "2.8.3"
|
|
125
135
|
},
|
|
126
136
|
"peerDependencies": {
|
|
137
|
+
"@langchain/anthropic": "^0.3.33",
|
|
138
|
+
"@langchain/deepseek": "^0.1.0",
|
|
139
|
+
"@langchain/google-genai": "^0.2.18",
|
|
140
|
+
"@langchain/openai": "^0.6.16",
|
|
127
141
|
"express": "^5.2.1",
|
|
128
142
|
"passport-azure-ad": "^4.3.5",
|
|
129
143
|
"passport-google-oauth20": "^2.0.0",
|
|
130
144
|
"react": "18.3.1",
|
|
131
145
|
"react-i18next": "^16.3.4",
|
|
132
|
-
"react-query": "^3.39.3"
|
|
133
|
-
"@langchain/anthropic": "^0.3.33",
|
|
134
|
-
"@langchain/deepseek": "^0.1.0",
|
|
135
|
-
"@langchain/google-genai": "^0.2.18",
|
|
136
|
-
"@langchain/openai": "^0.6.16"
|
|
146
|
+
"react-query": "^3.39.3"
|
|
137
147
|
},
|
|
138
148
|
"devDependencies": {
|
|
139
|
-
"@eslint/js": "^9.
|
|
149
|
+
"@eslint/js": "^9.39.4",
|
|
140
150
|
"concurrently": "^9.2.0",
|
|
141
151
|
"cross-env": "^10.0.0",
|
|
142
|
-
"eslint": "^9.
|
|
152
|
+
"eslint": "^9.39.4",
|
|
143
153
|
"globals": "^16.3.0"
|
|
144
154
|
},
|
|
145
155
|
"subPackages": [
|
package/src/ai.jobs.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import {parseSafeJSON} from "./core.js";
|
|
2
|
+
|
|
3
|
+
export const generateModelViaAI = async (llm, lang, txt, history, existingModels = []) => {
|
|
4
|
+
const conditionBuilderExample = JSON.stringify({ "$and": [ { "fieldNameOfTypeRelation": { "$find": { "$eq": [ "$$this.code", "fr" ] } }}, {"$eq":["$fieldOtherTypes","valueToSearch"]}, { "$ne": { "$user": null } } ] }, null, 2);
|
|
5
|
+
|
|
6
|
+
try {
|
|
7
|
+
const messages = [
|
|
8
|
+
{
|
|
9
|
+
role: "system",
|
|
10
|
+
content:
|
|
11
|
+
`
|
|
12
|
+
Tu dois me retourner un objet JSON valide contenant les suggestions de modèles, basés sur la demande de l'utilisateur.
|
|
13
|
+
La spécification JSON d'UN modèle de données est la suivante :
|
|
14
|
+
Par exemple, pour "un modèle pour gérer une bibliothèque et ses livres", la réponse pourrait être:
|
|
15
|
+
{
|
|
16
|
+
"models": [{
|
|
17
|
+
"name": "book",
|
|
18
|
+
"icon": "FaBook",
|
|
19
|
+
"description": "*description détaillée en plusieurs phrases de l'utilité du modèle, et ses cas d'usage, ici : Modèle de référencement de livres multi-support. Peut être utilisé pour des bibliothèques personnelles, virtuelles ou municipales.*",
|
|
20
|
+
"fields": [
|
|
21
|
+
{"name": "title", "type": "string", "required": true, "asMain": true, "hint": "Titre du livre", color: '#FF89CC'},
|
|
22
|
+
{"name": "isbn", "type": "string", "unique": true, "hint": "Numéro ISBN"},
|
|
23
|
+
{"name": "author", "type": "relation", "relation": "author", "hint": "Auteur du livre", color: '#CCCCCC'},
|
|
24
|
+
{"name": "publicationDate", "type": "date", "default": "now", "hint": "Date de publication du livre"}
|
|
25
|
+
{"name": "bankCardNumber", "type": "text", "anonymized": true, "hint": "Numéro de carte bancaire anonymisé"}
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
{
|
|
29
|
+
name: "library",
|
|
30
|
+
"description": "",
|
|
31
|
+
"icon": "FaBookOpenReader",
|
|
32
|
+
fields: [
|
|
33
|
+
{
|
|
34
|
+
"name": "name",
|
|
35
|
+
"type": "string",
|
|
36
|
+
asMain: true,
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"name": "address",
|
|
40
|
+
"type": "string",
|
|
41
|
+
asMain: true,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"name": "city",
|
|
45
|
+
"type": "string",
|
|
46
|
+
asMain: true,
|
|
47
|
+
color: '#E63345'
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "postalCode",
|
|
51
|
+
"type": "string",
|
|
52
|
+
asMain: true,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "country",
|
|
56
|
+
"type": "string",
|
|
57
|
+
color: "#4F80BF",
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
les types de champ autorisés sont :
|
|
64
|
+
string, string_t (translation key)
|
|
65
|
+
avec les propriétés { maxlength: number, multiline: boolean }
|
|
66
|
+
|
|
67
|
+
, password, url, phone, email,, richtext (html),
|
|
68
|
+
avec les propriétés { maxlength: number }
|
|
69
|
+
boolean
|
|
70
|
+
code
|
|
71
|
+
avec les propriétés { language: 'json', conditionBuilder: boolean }, avec conditionBuilder la prop pour construire des conditions sur les données
|
|
72
|
+
number
|
|
73
|
+
avec les propriétés { step: 0.1, unit: string, min: number, max: number }
|
|
74
|
+
|
|
75
|
+
date, datetime,
|
|
76
|
+
avec les propriétés { min: dateIso, max: dateIso }
|
|
77
|
+
enum,
|
|
78
|
+
avec les propriétés { items: ["label 1", "..."] }
|
|
79
|
+
|
|
80
|
+
array
|
|
81
|
+
avec les propriétés { itemsType: "string_t", ...et les autres propriétés du type itemsType }
|
|
82
|
+
relation
|
|
83
|
+
avec les propriétés { relationFilter: <condition>, multiple: boolean, relation: 'autreModeleNom'}, multiple permettant une relation 1-n pour chaque donnée du modèle.
|
|
84
|
+
Préfères l'utilisation du multiple et d'un modèle séparé plutôt qu'un type array avec itemsType=string.
|
|
85
|
+
Pour le type \`relation\`, utilise l'un des noms de modèles existants suivants : ${existingModels.join(', ')} ou le nom du modèle relationnel généré.\`
|
|
86
|
+
color
|
|
87
|
+
avec une valeur en héxadecimal, ex: '#FF0000'
|
|
88
|
+
file
|
|
89
|
+
pour gérer les documents
|
|
90
|
+
calculated
|
|
91
|
+
pour gérer les champs calculés (utile pour l'utilisateur)
|
|
92
|
+
model (nom du modele choisi),
|
|
93
|
+
|
|
94
|
+
cronSchedule
|
|
95
|
+
avec les propriétés { default: '* * * * * *', cronMask: [false, true, true, true, true, true])
|
|
96
|
+
|
|
97
|
+
<condition> ou la propriété 'condition' d'affichage sur les champs du modèle se définissent de cette manière :
|
|
98
|
+
${conditionBuilderExample} avec les opérateurs mongodb $eq,$ne,$gt,$gte,$lt,$lte,$in,$nin,$dateAdd disponibles
|
|
99
|
+
|
|
100
|
+
Les modèles doivent être absolument nommés autrement que ceux déjà existants (donc pas ceux là : {${existingModels.join(', ')})
|
|
101
|
+
Si ils sont nommés pareil, renomme les tiens avec un suffixe '_2', ou '_3'...
|
|
102
|
+
Et assures-toi de générer les modèles que tu as introduits dans tes relations.
|
|
103
|
+
|
|
104
|
+
'default' est la valeur par défaut que prendra le champ (string, number, boolean, or array of type supported)
|
|
105
|
+
Traduits le texte des hint et les données qui ne sont pas des variables ou des clés d'objet dans la langue iso2 ${lang}.
|
|
106
|
+
|
|
107
|
+
Mets obligatoirement des couleurs harmonieuses sur les champs asMain, ou ceux que tu considères comme importants, car ils doivent être mis en avant (selon le thème).
|
|
108
|
+
|
|
109
|
+
Ne renvoie bien QUE le tableau de modèles JSON, sans texte explicatif ni formatage markdown.`
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
role: "user",
|
|
113
|
+
content: typeof(txt) === 'string' ? txt.substring(0, 4096) : ''
|
|
114
|
+
},
|
|
115
|
+
...history
|
|
116
|
+
];
|
|
117
|
+
|
|
118
|
+
const completion = await llm.invoke(messages);
|
|
119
|
+
|
|
120
|
+
const aiResponse = completion.content;
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
// parseSafeJSON fonctionnera que le contenu soit un objet ou un tableau
|
|
124
|
+
return parseSafeJSON(aiResponse); // On retourne directement le résultat parsé
|
|
125
|
+
} catch (e) {
|
|
126
|
+
console.error("Erreur de parsing du JSON de l'IA:", e);
|
|
127
|
+
throw new Error("Réponse invalide de l'IA.");
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
} catch (error) {
|
|
131
|
+
console.error("Error calling OpenAI or parsing response:", error);
|
|
132
|
+
// Renvoyer l'erreur pour que l'endpoint API puisse la gérer
|
|
133
|
+
throw error;
|
|
134
|
+
}
|
|
135
|
+
};
|