@intranefr/superbackend 1.5.1 → 1.5.3
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/.env.example +10 -0
- package/index.js +2 -0
- package/manage.js +745 -0
- package/package.json +5 -2
- package/src/controllers/admin.controller.js +79 -6
- package/src/controllers/adminAgents.controller.js +37 -0
- package/src/controllers/adminExperiments.controller.js +200 -0
- package/src/controllers/adminLlm.controller.js +19 -0
- package/src/controllers/adminMarkdowns.controller.js +157 -0
- package/src/controllers/adminScripts.controller.js +243 -74
- package/src/controllers/adminTelegram.controller.js +72 -0
- package/src/controllers/experiments.controller.js +85 -0
- package/src/controllers/internalExperiments.controller.js +17 -0
- package/src/controllers/markdowns.controller.js +42 -0
- package/src/helpers/mongooseHelper.js +258 -0
- package/src/helpers/scriptBase.js +230 -0
- package/src/helpers/scriptRunner.js +335 -0
- package/src/middleware.js +195 -34
- package/src/models/Agent.js +105 -0
- package/src/models/AgentMessage.js +82 -0
- package/src/models/CacheEntry.js +1 -1
- package/src/models/ConsoleLog.js +1 -1
- package/src/models/Experiment.js +75 -0
- package/src/models/ExperimentAssignment.js +23 -0
- package/src/models/ExperimentEvent.js +26 -0
- package/src/models/ExperimentMetricBucket.js +30 -0
- package/src/models/GlobalSetting.js +1 -2
- package/src/models/Markdown.js +75 -0
- package/src/models/RateLimitCounter.js +1 -1
- package/src/models/ScriptDefinition.js +1 -0
- package/src/models/ScriptRun.js +8 -0
- package/src/models/TelegramBot.js +42 -0
- package/src/models/Webhook.js +2 -0
- package/src/routes/admin.routes.js +2 -0
- package/src/routes/adminAgents.routes.js +13 -0
- package/src/routes/adminConsoleManager.routes.js +1 -1
- package/src/routes/adminExperiments.routes.js +29 -0
- package/src/routes/adminLlm.routes.js +1 -0
- package/src/routes/adminMarkdowns.routes.js +16 -0
- package/src/routes/adminScripts.routes.js +4 -1
- package/src/routes/adminTelegram.routes.js +14 -0
- package/src/routes/blogInternal.routes.js +2 -2
- package/src/routes/experiments.routes.js +30 -0
- package/src/routes/internalExperiments.routes.js +15 -0
- package/src/routes/markdowns.routes.js +16 -0
- package/src/services/agent.service.js +546 -0
- package/src/services/agentHistory.service.js +345 -0
- package/src/services/agentTools.service.js +578 -0
- package/src/services/blogCronsBootstrap.service.js +7 -6
- package/src/services/consoleManager.service.js +56 -18
- package/src/services/consoleOverride.service.js +1 -0
- package/src/services/experiments.service.js +273 -0
- package/src/services/experimentsAggregation.service.js +308 -0
- package/src/services/experimentsCronsBootstrap.service.js +118 -0
- package/src/services/experimentsRetention.service.js +43 -0
- package/src/services/experimentsWs.service.js +134 -0
- package/src/services/globalSettings.service.js +15 -0
- package/src/services/jsonConfigs.service.js +24 -12
- package/src/services/llm.service.js +219 -6
- package/src/services/markdowns.service.js +522 -0
- package/src/services/scriptsRunner.service.js +514 -23
- package/src/services/telegram.service.js +130 -0
- package/src/utils/rbac/rightsRegistry.js +4 -0
- package/views/admin-agents.ejs +273 -0
- package/views/admin-coolify-deploy.ejs +8 -8
- package/views/admin-dashboard.ejs +63 -12
- package/views/admin-experiments.ejs +91 -0
- package/views/admin-markdowns.ejs +905 -0
- package/views/admin-scripts.ejs +817 -6
- package/views/admin-telegram.ejs +269 -0
- package/views/partials/dashboard/nav-items.ejs +4 -0
- package/views/partials/dashboard/palette.ejs +5 -3
- package/src/middleware/internalCronAuth.js +0 -29
package/src/middleware.js
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
const consoleOverride = require("./services/consoleOverride.service");
|
|
2
|
-
const consoleManager = require("./services/consoleManager.service");
|
|
2
|
+
const { consoleManager } = require("./services/consoleManager.service");
|
|
3
3
|
|
|
4
4
|
// Initialize console override service early to capture all logs
|
|
5
5
|
// Avoid keeping timers/streams alive during Jest runs.
|
|
6
6
|
if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
|
|
7
7
|
consoleOverride.init()
|
|
8
|
+
|
|
9
|
+
// Initialize console manager after a short delay to ensure consoleOverride is fully set up
|
|
10
|
+
setTimeout(() => {
|
|
11
|
+
// Set module prefix for this middleware
|
|
12
|
+
consoleManager.setModulePrefix('middleware');
|
|
13
|
+
|
|
14
|
+
// Initialize console manager early to enable prefixing for all subsequent logs
|
|
15
|
+
consoleManager.init();
|
|
16
|
+
console.log("[Console Manager] Initialized - prefixing enabled");
|
|
17
|
+
}, 20);
|
|
8
18
|
}
|
|
9
19
|
|
|
10
20
|
const express = require("express");
|
|
@@ -119,6 +129,11 @@ function createMiddleware(options = {}) {
|
|
|
119
129
|
attachTerminalWebsocketServer,
|
|
120
130
|
} = require("./services/terminalsWs.service");
|
|
121
131
|
attachTerminalWebsocketServer(server, { basePathPrefix: adminPath });
|
|
132
|
+
|
|
133
|
+
const {
|
|
134
|
+
attachExperimentsWebsocketServer,
|
|
135
|
+
} = require("./services/experimentsWs.service");
|
|
136
|
+
attachExperimentsWebsocketServer(server);
|
|
122
137
|
};
|
|
123
138
|
|
|
124
139
|
if (!errorCaptureInitialized) {
|
|
@@ -146,6 +161,8 @@ function createMiddleware(options = {}) {
|
|
|
146
161
|
maxPoolSize: 10,
|
|
147
162
|
};
|
|
148
163
|
|
|
164
|
+
const telegramService = require("./services/telegram.service");
|
|
165
|
+
|
|
149
166
|
// Return a promise that resolves when connection is established
|
|
150
167
|
const connectionPromise = mongoose
|
|
151
168
|
.connect(mongoUri, connectionOptions)
|
|
@@ -156,17 +173,13 @@ function createMiddleware(options = {}) {
|
|
|
156
173
|
await healthChecksScheduler.start();
|
|
157
174
|
await healthChecksBootstrap.bootstrap();
|
|
158
175
|
await blogCronsBootstrap.bootstrap();
|
|
176
|
+
await require("./services/experimentsCronsBootstrap.service").bootstrap();
|
|
159
177
|
|
|
160
|
-
// Initialize
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
console.log("[Console Manager] Initialized");
|
|
166
|
-
} else {
|
|
167
|
-
console.log("[Console Manager] Disabled - console methods not overridden");
|
|
168
|
-
}
|
|
169
|
-
}
|
|
178
|
+
// Initialize Telegram bots
|
|
179
|
+
await telegramService.init();
|
|
180
|
+
|
|
181
|
+
// Console manager is already initialized early in the middleware
|
|
182
|
+
console.log("[Console Manager] MongoDB connection established");
|
|
170
183
|
|
|
171
184
|
return true;
|
|
172
185
|
})
|
|
@@ -254,12 +267,20 @@ function createMiddleware(options = {}) {
|
|
|
254
267
|
blogCronsBootstrap.bootstrap().catch((err) => {
|
|
255
268
|
console.error("Failed to bootstrap blog crons:", err);
|
|
256
269
|
});
|
|
270
|
+
|
|
271
|
+
require("./services/experimentsCronsBootstrap.service")
|
|
272
|
+
.bootstrap()
|
|
273
|
+
.catch((err) => {
|
|
274
|
+
console.error("Failed to bootstrap experiments crons:", err);
|
|
275
|
+
});
|
|
257
276
|
|
|
258
277
|
// Initialize console manager AFTER database is already connected
|
|
259
278
|
if (process.env.NODE_ENV !== "test" && !process.env.JEST_WORKER_ID) {
|
|
260
279
|
isConsoleManagerEnabled().then(consoleManagerEnabled => {
|
|
261
280
|
if (consoleManagerEnabled) {
|
|
262
281
|
consoleManager.init();
|
|
282
|
+
// Set module prefix after initialization
|
|
283
|
+
consoleManager.setModulePrefix('middleware');
|
|
263
284
|
console.log("[Console Manager] Initialized");
|
|
264
285
|
} else {
|
|
265
286
|
console.log("[Console Manager] Disabled - console methods not overridden");
|
|
@@ -268,6 +289,8 @@ function createMiddleware(options = {}) {
|
|
|
268
289
|
console.error("[Console Manager] Error checking enabled status:", error);
|
|
269
290
|
console.log("[Console Manager] Fallback to enabled due to error");
|
|
270
291
|
consoleManager.init();
|
|
292
|
+
// Set module prefix after initialization
|
|
293
|
+
consoleManager.setModulePrefix('middleware');
|
|
271
294
|
console.log("[Console Manager] Initialized (fallback)");
|
|
272
295
|
});
|
|
273
296
|
}
|
|
@@ -467,8 +490,13 @@ function createMiddleware(options = {}) {
|
|
|
467
490
|
});
|
|
468
491
|
});
|
|
469
492
|
|
|
470
|
-
router.get(`${adminPath}/
|
|
471
|
-
const templatePath = path.join(
|
|
493
|
+
router.get(`${adminPath}/experiments`, basicAuth, (req, res) => {
|
|
494
|
+
const templatePath = path.join(
|
|
495
|
+
__dirname,
|
|
496
|
+
"..",
|
|
497
|
+
"views",
|
|
498
|
+
"admin-experiments.ejs",
|
|
499
|
+
);
|
|
472
500
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
473
501
|
if (err) {
|
|
474
502
|
console.error("Error reading template:", err);
|
|
@@ -493,13 +521,8 @@ function createMiddleware(options = {}) {
|
|
|
493
521
|
});
|
|
494
522
|
});
|
|
495
523
|
|
|
496
|
-
router.get(`${adminPath}/
|
|
497
|
-
const templatePath = path.join(
|
|
498
|
-
__dirname,
|
|
499
|
-
"..",
|
|
500
|
-
"views",
|
|
501
|
-
"admin-terminals.ejs",
|
|
502
|
-
);
|
|
524
|
+
router.get(`${adminPath}/rbac`, basicAuth, (req, res) => {
|
|
525
|
+
const templatePath = path.join(__dirname, "..", "views", "admin-rbac.ejs");
|
|
503
526
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
504
527
|
if (err) {
|
|
505
528
|
console.error("Error reading template:", err);
|
|
@@ -511,7 +534,6 @@ function createMiddleware(options = {}) {
|
|
|
511
534
|
{
|
|
512
535
|
baseUrl: req.baseUrl,
|
|
513
536
|
adminPath,
|
|
514
|
-
endpointRegistry,
|
|
515
537
|
},
|
|
516
538
|
{
|
|
517
539
|
filename: templatePath,
|
|
@@ -525,12 +547,12 @@ function createMiddleware(options = {}) {
|
|
|
525
547
|
});
|
|
526
548
|
});
|
|
527
549
|
|
|
528
|
-
router.get(`${adminPath}/
|
|
550
|
+
router.get(`${adminPath}/terminals`, basicAuth, (req, res) => {
|
|
529
551
|
const templatePath = path.join(
|
|
530
552
|
__dirname,
|
|
531
553
|
"..",
|
|
532
554
|
"views",
|
|
533
|
-
"admin-
|
|
555
|
+
"admin-terminals.ejs",
|
|
534
556
|
);
|
|
535
557
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
536
558
|
if (err) {
|
|
@@ -557,8 +579,13 @@ function createMiddleware(options = {}) {
|
|
|
557
579
|
});
|
|
558
580
|
});
|
|
559
581
|
|
|
560
|
-
router.get(`${adminPath}/
|
|
561
|
-
const templatePath = path.join(
|
|
582
|
+
router.get(`${adminPath}/scripts`, basicAuth, (req, res) => {
|
|
583
|
+
const templatePath = path.join(
|
|
584
|
+
__dirname,
|
|
585
|
+
"..",
|
|
586
|
+
"views",
|
|
587
|
+
"admin-scripts.ejs",
|
|
588
|
+
);
|
|
562
589
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
563
590
|
if (err) {
|
|
564
591
|
console.error("Error reading template:", err);
|
|
@@ -570,6 +597,7 @@ function createMiddleware(options = {}) {
|
|
|
570
597
|
{
|
|
571
598
|
baseUrl: req.baseUrl,
|
|
572
599
|
adminPath,
|
|
600
|
+
endpointRegistry,
|
|
573
601
|
},
|
|
574
602
|
{
|
|
575
603
|
filename: templatePath,
|
|
@@ -583,8 +611,8 @@ function createMiddleware(options = {}) {
|
|
|
583
611
|
});
|
|
584
612
|
});
|
|
585
613
|
|
|
586
|
-
router.get(`${adminPath}/
|
|
587
|
-
const templatePath = path.join(__dirname, "..", "views", "admin-
|
|
614
|
+
router.get(`${adminPath}/crons`, basicAuth, (req, res) => {
|
|
615
|
+
const templatePath = path.join(__dirname, "..", "views", "admin-crons.ejs");
|
|
588
616
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
589
617
|
if (err) {
|
|
590
618
|
console.error("Error reading template:", err);
|
|
@@ -609,13 +637,8 @@ function createMiddleware(options = {}) {
|
|
|
609
637
|
});
|
|
610
638
|
});
|
|
611
639
|
|
|
612
|
-
router.get(`${adminPath}/
|
|
613
|
-
const templatePath = path.join(
|
|
614
|
-
__dirname,
|
|
615
|
-
"..",
|
|
616
|
-
"views",
|
|
617
|
-
"admin-db-browser.ejs",
|
|
618
|
-
);
|
|
640
|
+
router.get(`${adminPath}/cache`, basicAuth, (req, res) => {
|
|
641
|
+
const templatePath = path.join(__dirname, "..", "views", "admin-cache.ejs");
|
|
619
642
|
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
620
643
|
if (err) {
|
|
621
644
|
console.error("Error reading template:", err);
|
|
@@ -640,6 +663,99 @@ function createMiddleware(options = {}) {
|
|
|
640
663
|
});
|
|
641
664
|
});
|
|
642
665
|
|
|
666
|
+
router.get(`${adminPath}/db-browser`, basicAuth, (req, res) => {
|
|
667
|
+
const templatePath = path.join(
|
|
668
|
+
__dirname,
|
|
669
|
+
"..",
|
|
670
|
+
"views",
|
|
671
|
+
"admin-db-browser.ejs",
|
|
672
|
+
);
|
|
673
|
+
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
674
|
+
if (err) {
|
|
675
|
+
console.error("Error reading template:", err);
|
|
676
|
+
return res.status(500).send("Error loading page");
|
|
677
|
+
}
|
|
678
|
+
try {
|
|
679
|
+
const html = ejs.render(
|
|
680
|
+
template,
|
|
681
|
+
{
|
|
682
|
+
baseUrl: req.baseUrl,
|
|
683
|
+
adminPath,
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
filename: templatePath,
|
|
687
|
+
},
|
|
688
|
+
);
|
|
689
|
+
res.send(html);
|
|
690
|
+
} catch (renderErr) {
|
|
691
|
+
console.error("Error rendering template:", renderErr);
|
|
692
|
+
res.status(500).send("Error rendering page");
|
|
693
|
+
}
|
|
694
|
+
});
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
router.get(`${adminPath}/telegram`, basicAuth, (req, res) => {
|
|
698
|
+
const templatePath = path.join(
|
|
699
|
+
__dirname,
|
|
700
|
+
"..",
|
|
701
|
+
"views",
|
|
702
|
+
"admin-telegram.ejs",
|
|
703
|
+
);
|
|
704
|
+
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
705
|
+
if (err) {
|
|
706
|
+
console.error("Error reading template:", err);
|
|
707
|
+
return res.status(500).send("Error loading page");
|
|
708
|
+
}
|
|
709
|
+
try {
|
|
710
|
+
const html = ejs.render(
|
|
711
|
+
template,
|
|
712
|
+
{
|
|
713
|
+
baseUrl: req.baseUrl,
|
|
714
|
+
adminPath,
|
|
715
|
+
},
|
|
716
|
+
{
|
|
717
|
+
filename: templatePath,
|
|
718
|
+
},
|
|
719
|
+
);
|
|
720
|
+
res.send(html);
|
|
721
|
+
} catch (renderErr) {
|
|
722
|
+
console.error("Error rendering template:", renderErr);
|
|
723
|
+
res.status(500).send("Error rendering page");
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
|
|
728
|
+
router.get(`${adminPath}/agents`, basicAuth, (req, res) => {
|
|
729
|
+
const templatePath = path.join(
|
|
730
|
+
__dirname,
|
|
731
|
+
"..",
|
|
732
|
+
"views",
|
|
733
|
+
"admin-agents.ejs",
|
|
734
|
+
);
|
|
735
|
+
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
736
|
+
if (err) {
|
|
737
|
+
console.error("Error reading template:", err);
|
|
738
|
+
return res.status(500).send("Error loading page");
|
|
739
|
+
}
|
|
740
|
+
try {
|
|
741
|
+
const html = ejs.render(
|
|
742
|
+
template,
|
|
743
|
+
{
|
|
744
|
+
baseUrl: req.baseUrl,
|
|
745
|
+
adminPath,
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
filename: templatePath,
|
|
749
|
+
},
|
|
750
|
+
);
|
|
751
|
+
res.send(html);
|
|
752
|
+
} catch (renderErr) {
|
|
753
|
+
console.error("Error rendering template:", renderErr);
|
|
754
|
+
res.status(500).send("Error rendering page");
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
|
|
643
759
|
router.use("/api/admin", require("./routes/admin.routes"));
|
|
644
760
|
router.use("/api/admin/settings", require("./routes/globalSettings.routes"));
|
|
645
761
|
router.use(
|
|
@@ -650,6 +766,10 @@ function createMiddleware(options = {}) {
|
|
|
650
766
|
"/api/admin/json-configs",
|
|
651
767
|
require("./routes/adminJsonConfigs.routes"),
|
|
652
768
|
);
|
|
769
|
+
router.use(
|
|
770
|
+
"/api/admin/markdowns",
|
|
771
|
+
require("./routes/adminMarkdowns.routes"),
|
|
772
|
+
);
|
|
653
773
|
router.use(
|
|
654
774
|
"/api/admin/rate-limits",
|
|
655
775
|
require("./routes/adminRateLimits.routes"),
|
|
@@ -677,6 +797,7 @@ function createMiddleware(options = {}) {
|
|
|
677
797
|
require("./routes/adminDbBrowser.routes"),
|
|
678
798
|
);
|
|
679
799
|
router.use("/api/admin/terminals", require("./routes/adminTerminals.routes"));
|
|
800
|
+
router.use("/api/admin/experiments", require("./routes/adminExperiments.routes"));
|
|
680
801
|
router.use("/api/admin/assets", require("./routes/adminAssets.routes"));
|
|
681
802
|
router.use(
|
|
682
803
|
"/api/admin/upload-namespaces",
|
|
@@ -698,6 +819,8 @@ function createMiddleware(options = {}) {
|
|
|
698
819
|
require("./routes/adminAudit.routes"),
|
|
699
820
|
);
|
|
700
821
|
router.use("/api/admin/llm", require("./routes/adminLlm.routes"));
|
|
822
|
+
router.use("/api/admin/telegram", require("./routes/adminTelegram.routes"));
|
|
823
|
+
router.use("/api/admin/agents", require("./routes/adminAgents.routes"));
|
|
701
824
|
router.use(
|
|
702
825
|
"/api/admin/ejs-virtual",
|
|
703
826
|
require("./routes/adminEjsVirtual.routes"),
|
|
@@ -712,6 +835,7 @@ function createMiddleware(options = {}) {
|
|
|
712
835
|
router.use("/api/settings", require("./routes/globalSettings.routes"));
|
|
713
836
|
router.use("/api/feature-flags", require("./routes/featureFlags.routes"));
|
|
714
837
|
router.use("/api/json-configs", require("./routes/jsonConfigs.routes"));
|
|
838
|
+
router.use("/api/markdowns", require("./routes/markdowns.routes"));
|
|
715
839
|
router.use("/api/assets", require("./routes/assets.routes"));
|
|
716
840
|
router.use("/api/i18n", require("./routes/i18n.routes"));
|
|
717
841
|
router.use("/api/headless", require("./routes/headless.routes"));
|
|
@@ -724,6 +848,7 @@ function createMiddleware(options = {}) {
|
|
|
724
848
|
router.use("/api/ui-components", require("./routes/uiComponentsPublic.routes"));
|
|
725
849
|
router.use("/api/rbac", require("./routes/rbac.routes"));
|
|
726
850
|
router.use("/api/file-manager", require("./routes/fileManager.routes"));
|
|
851
|
+
router.use("/api/experiments", require("./routes/experiments.routes"));
|
|
727
852
|
|
|
728
853
|
// Public blog APIs (headless)
|
|
729
854
|
router.use("/api", require("./routes/blogPublic.routes"));
|
|
@@ -731,6 +856,9 @@ function createMiddleware(options = {}) {
|
|
|
731
856
|
// Internal blog endpoints (used by HTTP CronJobs)
|
|
732
857
|
router.use("/api/internal", require("./routes/blogInternal.routes"));
|
|
733
858
|
|
|
859
|
+
// Internal experiments endpoints (used by HTTP CronJobs)
|
|
860
|
+
router.use("/api/internal", require("./routes/internalExperiments.routes"));
|
|
861
|
+
|
|
734
862
|
// Public health checks status (gated by global setting)
|
|
735
863
|
router.use(
|
|
736
864
|
"/api/health-checks",
|
|
@@ -1311,6 +1439,39 @@ function createMiddleware(options = {}) {
|
|
|
1311
1439
|
});
|
|
1312
1440
|
});
|
|
1313
1441
|
|
|
1442
|
+
// Admin markdowns page (protected by basic auth)
|
|
1443
|
+
router.get(`${adminPath}/markdowns`, basicAuth, (req, res) => {
|
|
1444
|
+
const templatePath = path.join(
|
|
1445
|
+
__dirname,
|
|
1446
|
+
"..",
|
|
1447
|
+
"views",
|
|
1448
|
+
"admin-markdowns.ejs",
|
|
1449
|
+
);
|
|
1450
|
+
fs.readFile(templatePath, "utf8", (err, template) => {
|
|
1451
|
+
if (err) {
|
|
1452
|
+
console.error("Error reading template:", err);
|
|
1453
|
+
return res.status(500).send("Error loading page");
|
|
1454
|
+
}
|
|
1455
|
+
try {
|
|
1456
|
+
const html = ejs.render(
|
|
1457
|
+
template,
|
|
1458
|
+
{
|
|
1459
|
+
baseUrl: req.baseUrl,
|
|
1460
|
+
adminPath,
|
|
1461
|
+
endpointRegistry,
|
|
1462
|
+
},
|
|
1463
|
+
{
|
|
1464
|
+
filename: templatePath,
|
|
1465
|
+
},
|
|
1466
|
+
);
|
|
1467
|
+
res.send(html);
|
|
1468
|
+
} catch (renderErr) {
|
|
1469
|
+
console.error("Error rendering template:", renderErr);
|
|
1470
|
+
res.status(500).send("Error rendering page");
|
|
1471
|
+
}
|
|
1472
|
+
});
|
|
1473
|
+
});
|
|
1474
|
+
|
|
1314
1475
|
// Admin assets page (protected by basic auth)
|
|
1315
1476
|
router.get(`${adminPath}/assets`, basicAuth, (req, res) => {
|
|
1316
1477
|
const templatePath = path.join(
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const agentSchema = new mongoose.Schema({
|
|
4
|
+
name: {
|
|
5
|
+
type: String,
|
|
6
|
+
required: true,
|
|
7
|
+
trim: true
|
|
8
|
+
},
|
|
9
|
+
systemPrompt: {
|
|
10
|
+
type: String,
|
|
11
|
+
default: `You are a helpful assistant with access to specific tools for querying data.
|
|
12
|
+
|
|
13
|
+
AVAILABLE TOOLS:
|
|
14
|
+
|
|
15
|
+
1. query_database: Query the MongoDB database for insights.
|
|
16
|
+
- Parameters:
|
|
17
|
+
- modelName (required): The name of the Mongoose model (e.g., User, Markdown, AuditEvent)
|
|
18
|
+
- query (required): The MongoDB query object
|
|
19
|
+
- limit (optional): Limit the number of results (default: 5)
|
|
20
|
+
- Usage: Use this when you need to fetch specific data from the database.
|
|
21
|
+
|
|
22
|
+
2. get_system_stats: Get general statistics about the system.
|
|
23
|
+
- Parameters: None
|
|
24
|
+
- Usage: Use this when you need overall counts of users, markdowns, and other system entities.
|
|
25
|
+
|
|
26
|
+
3. raw_db_query: Execute raw MongoDB queries for database exploration.
|
|
27
|
+
- Parameters:
|
|
28
|
+
- queryType (required): The type of raw query to execute
|
|
29
|
+
• listDatabases: List all databases (requires admin access)
|
|
30
|
+
• listCollections: List all collections in a database
|
|
31
|
+
• countDocuments: Count documents in a collection
|
|
32
|
+
• findOne: Find a single document in a collection
|
|
33
|
+
• aggregate: Run aggregation pipeline
|
|
34
|
+
• adminCommand: Execute admin commands
|
|
35
|
+
- database (optional): Database name (defaults to current database)
|
|
36
|
+
- collection (required for collection queries): Collection name
|
|
37
|
+
- filter (optional): MongoDB filter/query object. Can be:
|
|
38
|
+
• A JSON object: { createdAt: { $gte: new Date() } }
|
|
39
|
+
• A JSON string: '{"createdAt": {"$gte": {"$date": "2024-01-01"}}}'
|
|
40
|
+
• For aggregate: an array of pipeline stages as object or JSON string
|
|
41
|
+
- limit (optional): Limit results (default: 10)
|
|
42
|
+
- adminCommand (optional): Admin command for adminCommand queryType (as object or JSON string)
|
|
43
|
+
- Usage: Use this to discover collection names, databases, or run admin commands.
|
|
44
|
+
- IMPORTANT: For complex queries, use JSON string format to avoid parsing issues
|
|
45
|
+
|
|
46
|
+
IMPORTANT ERROR HANDLING INSTRUCTIONS:
|
|
47
|
+
- When a tool returns an error, it will be in structured JSON format with error details
|
|
48
|
+
- ALWAYS provide a friendly, conversational response to the user about tool errors
|
|
49
|
+
- NEVER show raw error JSON to users
|
|
50
|
+
- DO: "I had trouble accessing the database. Let me try a different approach..."
|
|
51
|
+
- DO NOT: Show the actual error JSON to users
|
|
52
|
+
- Extract the error message and provide helpful suggestions based on the error context
|
|
53
|
+
- If an error is not recoverable, explain why and suggest alternatives
|
|
54
|
+
- If an error is recoverable, explain what you'll try next
|
|
55
|
+
- Use the error suggestions provided in the tool response to inform your response
|
|
56
|
+
|
|
57
|
+
INSTRUCTIONS:
|
|
58
|
+
- Always use tools when you need actual data from the database
|
|
59
|
+
- Never make up data or statistics
|
|
60
|
+
- For database queries, use exact model names as they appear in the system
|
|
61
|
+
- When using query_database, construct appropriate MongoDB query objects based on the user's request
|
|
62
|
+
- If you don't have enough information for a query, ask clarifying questions
|
|
63
|
+
- Use get_system_stats for high-level overview requests
|
|
64
|
+
- Use raw_db_query for:
|
|
65
|
+
* Discovering what collections exist: queryType: "listCollections"
|
|
66
|
+
* Finding database names: queryType: "listDatabases" (may require admin)
|
|
67
|
+
* Counting documents: queryType: "countDocuments" with collection and filter
|
|
68
|
+
* Exploring collection structure: queryType: "findOne" or "aggregate"
|
|
69
|
+
- For specific records, use query_database with appropriate filters
|
|
70
|
+
|
|
71
|
+
Respond helpfully and only use the tools when necessary for accurate information. Always provide friendly error messages to users when tools fail.`
|
|
72
|
+
},
|
|
73
|
+
providerKey: {
|
|
74
|
+
type: String,
|
|
75
|
+
required: true
|
|
76
|
+
},
|
|
77
|
+
model: {
|
|
78
|
+
type: String,
|
|
79
|
+
required: true
|
|
80
|
+
},
|
|
81
|
+
tools: {
|
|
82
|
+
type: [String],
|
|
83
|
+
default: []
|
|
84
|
+
},
|
|
85
|
+
temperature: {
|
|
86
|
+
type: Number,
|
|
87
|
+
default: 0.7
|
|
88
|
+
},
|
|
89
|
+
maxIterations: {
|
|
90
|
+
type: Number,
|
|
91
|
+
default: 10
|
|
92
|
+
},
|
|
93
|
+
orgId: {
|
|
94
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
95
|
+
ref: 'Organization'
|
|
96
|
+
},
|
|
97
|
+
ownerUserId: {
|
|
98
|
+
type: mongoose.Schema.Types.ObjectId,
|
|
99
|
+
ref: 'User'
|
|
100
|
+
}
|
|
101
|
+
}, {
|
|
102
|
+
timestamps: true
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
module.exports = mongoose.model('Agent', agentSchema);
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
const { ObjectId } = mongoose.Schema.Types;
|
|
3
|
+
|
|
4
|
+
const agentMessageSchema = new mongoose.Schema({
|
|
5
|
+
agentId: {
|
|
6
|
+
type: ObjectId,
|
|
7
|
+
ref: 'Agent',
|
|
8
|
+
required: true,
|
|
9
|
+
index: true
|
|
10
|
+
},
|
|
11
|
+
chatId: {
|
|
12
|
+
type: String,
|
|
13
|
+
required: true,
|
|
14
|
+
index: true
|
|
15
|
+
},
|
|
16
|
+
role: {
|
|
17
|
+
type: String,
|
|
18
|
+
enum: ['user', 'assistant', 'system', 'tool'],
|
|
19
|
+
required: true
|
|
20
|
+
},
|
|
21
|
+
content: {
|
|
22
|
+
type: String,
|
|
23
|
+
required: function() {
|
|
24
|
+
// Content is not required if:
|
|
25
|
+
// 1. Role is 'tool' (content might be in metadata or implied) - though usually tool has content
|
|
26
|
+
// 2. Role is 'assistant' AND it has toolCalls (OpenAI often returns null content with tool calls)
|
|
27
|
+
if (this.role === 'tool') return false;
|
|
28
|
+
if (this.role === 'assistant' && this.toolCalls && this.toolCalls.length > 0) return false;
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
toolCalls: [{
|
|
33
|
+
name: String,
|
|
34
|
+
arguments: mongoose.Schema.Types.Mixed,
|
|
35
|
+
toolCallId: String
|
|
36
|
+
}],
|
|
37
|
+
toolCallId: {
|
|
38
|
+
type: String,
|
|
39
|
+
index: true
|
|
40
|
+
},
|
|
41
|
+
metadata: {
|
|
42
|
+
tokens: Number,
|
|
43
|
+
processingTime: Number,
|
|
44
|
+
model: String,
|
|
45
|
+
provider: String,
|
|
46
|
+
timestamp: Date,
|
|
47
|
+
temperature: Number
|
|
48
|
+
},
|
|
49
|
+
createdAt: {
|
|
50
|
+
type: Date,
|
|
51
|
+
default: Date.now,
|
|
52
|
+
index: true
|
|
53
|
+
},
|
|
54
|
+
updatedAt: {
|
|
55
|
+
type: Date,
|
|
56
|
+
default: Date.now
|
|
57
|
+
}
|
|
58
|
+
}, {
|
|
59
|
+
timestamps: true,
|
|
60
|
+
toJSON: { virtuals: true },
|
|
61
|
+
toObject: { virtuals: true }
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Compound index for efficient session history retrieval
|
|
65
|
+
agentMessageSchema.index(
|
|
66
|
+
{ agentId: 1, chatId: 1, createdAt: 1 },
|
|
67
|
+
{ name: 'session_history_idx' }
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
// Index for tool call lookup
|
|
71
|
+
agentMessageSchema.index(
|
|
72
|
+
{ toolCallId: 1 },
|
|
73
|
+
{ name: 'tool_call_idx' }
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Index for searching content
|
|
77
|
+
agentMessageSchema.index(
|
|
78
|
+
{ content: 'text' },
|
|
79
|
+
{ name: 'content_search_idx' }
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
module.exports = mongoose.model('AgentMessage', agentMessageSchema);
|
package/src/models/CacheEntry.js
CHANGED
|
@@ -10,7 +10,7 @@ const cacheEntrySchema = new mongoose.Schema(
|
|
|
10
10
|
|
|
11
11
|
sizeBytes: { type: Number, default: 0 },
|
|
12
12
|
|
|
13
|
-
expiresAt: { type: Date, default: null
|
|
13
|
+
expiresAt: { type: Date, default: null },
|
|
14
14
|
|
|
15
15
|
hits: { type: Number, default: 0 },
|
|
16
16
|
lastAccessAt: { type: Date, default: null },
|
package/src/models/ConsoleLog.js
CHANGED
|
@@ -12,7 +12,7 @@ const consoleLogSchema = new mongoose.Schema(
|
|
|
12
12
|
requestId: { type: String, default: '', index: true },
|
|
13
13
|
|
|
14
14
|
createdAt: { type: Date, default: Date.now, index: true },
|
|
15
|
-
expiresAt: { type: Date, default: null
|
|
15
|
+
expiresAt: { type: Date, default: null },
|
|
16
16
|
},
|
|
17
17
|
{ timestamps: false, collection: 'console_logs' },
|
|
18
18
|
);
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const mongoose = require('mongoose');
|
|
2
|
+
|
|
3
|
+
const experimentVariantSchema = new mongoose.Schema(
|
|
4
|
+
{
|
|
5
|
+
key: { type: String, required: true },
|
|
6
|
+
weight: { type: Number, default: 0 },
|
|
7
|
+
configSlug: { type: String, default: '' },
|
|
8
|
+
},
|
|
9
|
+
{ _id: false },
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
const metricDefinitionSchema = new mongoose.Schema(
|
|
13
|
+
{
|
|
14
|
+
key: { type: String, required: true },
|
|
15
|
+
kind: { type: String, enum: ['count', 'sum', 'avg', 'rate'], default: 'count' },
|
|
16
|
+
numeratorEventKey: { type: String, default: '' },
|
|
17
|
+
denominatorEventKey: { type: String, default: '' },
|
|
18
|
+
objective: { type: String, enum: ['maximize', 'minimize'], default: 'maximize' },
|
|
19
|
+
},
|
|
20
|
+
{ _id: false },
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const winnerPolicySchema = new mongoose.Schema(
|
|
24
|
+
{
|
|
25
|
+
mode: { type: String, enum: ['manual', 'automatic'], default: 'manual' },
|
|
26
|
+
pickAfterMs: { type: Number, default: 0 },
|
|
27
|
+
minAssignments: { type: Number, default: 0 },
|
|
28
|
+
minExposures: { type: Number, default: 0 },
|
|
29
|
+
minConversions: { type: Number, default: 0 },
|
|
30
|
+
statMethod: { type: String, enum: ['simple_rate', 'bayesian_beta'], default: 'simple_rate' },
|
|
31
|
+
overrideWinnerVariantKey: { type: String, default: '' },
|
|
32
|
+
},
|
|
33
|
+
{ _id: false },
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
const experimentSchema = new mongoose.Schema(
|
|
37
|
+
{
|
|
38
|
+
organizationId: { type: mongoose.Schema.Types.ObjectId, ref: 'Organization', default: null, index: true },
|
|
39
|
+
|
|
40
|
+
code: { type: String, required: true },
|
|
41
|
+
name: { type: String, default: '' },
|
|
42
|
+
description: { type: String, default: '' },
|
|
43
|
+
|
|
44
|
+
status: { type: String, enum: ['draft', 'running', 'paused', 'completed'], default: 'draft', index: true },
|
|
45
|
+
|
|
46
|
+
startedAt: { type: Date, default: null },
|
|
47
|
+
endsAt: { type: Date, default: null },
|
|
48
|
+
|
|
49
|
+
assignment: {
|
|
50
|
+
unit: { type: String, enum: ['subjectId'], default: 'subjectId' },
|
|
51
|
+
sticky: { type: Boolean, default: true },
|
|
52
|
+
salt: { type: String, default: '' },
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
variants: { type: [experimentVariantSchema], default: [] },
|
|
56
|
+
|
|
57
|
+
primaryMetric: { type: metricDefinitionSchema, required: true },
|
|
58
|
+
secondaryMetrics: { type: [metricDefinitionSchema], default: [] },
|
|
59
|
+
|
|
60
|
+
winnerPolicy: { type: winnerPolicySchema, default: () => ({}) },
|
|
61
|
+
|
|
62
|
+
winnerVariantKey: { type: String, default: '' },
|
|
63
|
+
winnerDecidedAt: { type: Date, default: null },
|
|
64
|
+
winnerReason: { type: String, default: '' },
|
|
65
|
+
|
|
66
|
+
createdByUserId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', default: null },
|
|
67
|
+
updatedByUserId: { type: mongoose.Schema.Types.ObjectId, ref: 'User', default: null },
|
|
68
|
+
},
|
|
69
|
+
{ timestamps: true, collection: 'experiments' },
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
experimentSchema.index({ organizationId: 1, code: 1 }, { unique: true });
|
|
73
|
+
experimentSchema.index({ status: 1, startedAt: 1 });
|
|
74
|
+
|
|
75
|
+
module.exports = mongoose.models.Experiment || mongoose.model('Experiment', experimentSchema);
|