@yrpri/api 9.0.231 → 9.0.233
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/agents/managers/subscriptionManager.js +2 -2
- package/controllers/allOurIdeas.js +1 -1
- package/controllers/communities.cjs +282 -91
- package/controllers/domains.cjs +54 -8
- package/controllers/groups.cjs +51 -6
- package/controllers/points.cjs +4 -9
- package/controllers/posts.cjs +7 -9
- package/controllers/ratings.cjs +3 -6
- package/models/point.cjs +31 -3
- package/models/post.cjs +2 -3
- package/package.json +32 -67
- package/services/engine/allOurIdeas/aiHelper.d.ts +7 -4
- package/services/engine/allOurIdeas/aiHelper.js +34 -19
- package/services/engine/allOurIdeas/explainAnswersAssistant.d.ts +1 -1
- package/services/engine/allOurIdeas/explainAnswersAssistant.js +3 -9
- package/services/engine/moderation/fraud/CreateFraudAuditReport.cjs +35 -11
- package/services/engine/moderation/fraud/CreateFraudAuditReport.d.cts +21 -0
- package/services/engine/moderation/fraud/FraudBase.cjs +38 -18
- package/services/engine/moderation/fraud/FraudBase.d.cts +2 -0
- package/services/engine/moderation/fraud/FraudDeleteBase.cjs +48 -29
- package/services/engine/moderation/fraud/FraudDeleteBase.d.cts +8 -6
- package/services/engine/moderation/fraud/FraudDeleteEndorsements.cjs +5 -4
- package/services/engine/moderation/fraud/FraudDeleteEndorsements.d.cts +2 -2
- package/services/engine/moderation/fraud/FraudDeletePointQualities.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeletePointQualities.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudDeletePoints.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeletePoints.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudDeletePosts.cjs +3 -2
- package/services/engine/moderation/fraud/FraudDeleteRatings.cjs +61 -4
- package/services/engine/moderation/fraud/FraudGetBase.cjs +44 -20
- package/services/engine/moderation/fraud/FraudGetBase.d.cts +5 -0
- package/services/engine/moderation/fraud/FraudGetEndorsements.cjs +4 -13
- package/services/engine/moderation/fraud/FraudGetEndorsements.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPointQualities.cjs +3 -0
- package/services/engine/moderation/fraud/FraudGetPointQualities.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPoints.cjs +3 -0
- package/services/engine/moderation/fraud/FraudGetPoints.d.cts +1 -1
- package/services/engine/moderation/fraud/FraudGetPosts.cjs +17 -16
- package/services/engine/moderation/fraud/FraudGetPosts.d.cts +3 -3
- package/services/engine/moderation/fraud/FraudGetRatings.cjs +62 -30
- package/services/engine/moderation/fraud/FraudGetRatings.d.cts +4 -1
- package/services/engine/moderation/fraud/FraudRequestValidation.cjs +143 -0
- package/services/engine/moderation/fraud/FraudRequestValidation.d.cts +21 -0
- package/services/engine/moderation/fraud/FraudScannerNotifier.cjs +59 -35
- package/services/engine/moderation/fraud/FraudScannerNotifier.d.cts +20 -1
- package/services/llms/baseChatBot.d.ts +2 -0
- package/services/llms/baseChatBot.js +25 -9
- package/services/llms/imageGeneration/chatGptImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/chatGptImageGenerator.js +13 -10
- package/services/llms/imageGeneration/collectionImageGenerator.js +31 -13
- package/services/llms/imageGeneration/dalleImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/dalleImageGenerator.js +28 -16
- package/services/llms/imageGeneration/fluxImageGenerator.d.ts +2 -2
- package/services/llms/imageGeneration/fluxImageGenerator.js +9 -3
- package/services/llms/imageGeneration/iImageGenerator.d.ts +8 -1
- package/services/llms/imageGeneration/imageModelConfig.cjs +319 -0
- package/services/llms/imageGeneration/imageModelConfig.d.cts +79 -0
- package/services/llms/imageGeneration/imagenImageGenerator.d.ts +2 -3
- package/services/llms/imageGeneration/imagenImageGenerator.js +10 -10
- package/tests/fraudManagement.test.cjs +470 -0
- package/tests/fraudManagement.test.d.cts +1 -0
- package/tests/imageModelConfig.test.cjs +144 -0
- package/tests/imageModelConfig.test.d.cts +1 -0
- package/utils/ai_image_generation_guard.cjs +268 -0
- package/utils/ai_image_generation_guard.d.cts +34 -0
- package/utils/fingerprint_data.cjs +32 -0
- package/utils/fingerprint_data.d.cts +6 -0
- package/utils/recount_utils.cjs +53 -37
- package/utils/recount_utils.d.cts +7 -7
package/controllers/groups.cjs
CHANGED
|
@@ -24,6 +24,8 @@ const performSingleModerationAction = require("../services/engine/moderation/pro
|
|
|
24
24
|
const request = require("../utils/requestCompat.cjs");
|
|
25
25
|
const { updateAnswerTranslation, } = require("../services/utils/translation_helpers.cjs");
|
|
26
26
|
const { updateSurveyTranslation, } = require("../services/utils/translation_helpers.cjs");
|
|
27
|
+
const { normalizeImageGenerationProfileOptions, } = require("../services/llms/imageGeneration/imageModelConfig.cjs");
|
|
28
|
+
const { validateImageGenerationStartRequest, canPollImageGenerationJob, publicJobFields, } = require("../utils/ai_image_generation_guard.cjs");
|
|
27
29
|
const { plausibleStatsProxy, getPlausibleStats, } = require("../services/engine/analytics/plausible/manager.cjs");
|
|
28
30
|
const { countAllModeratedItemsByGroup, } = require("../services/engine/moderation/get_moderation_items.cjs");
|
|
29
31
|
const { isValidDbId } = require("../utils/is_valid_db_id.cjs");
|
|
@@ -1647,8 +1649,37 @@ router.get("/:groupId/:jobId/report_creation_progress", auth.can("edit group"),
|
|
|
1647
1649
|
});
|
|
1648
1650
|
});
|
|
1649
1651
|
//TODO: Fix this permission back to edit
|
|
1650
|
-
router.post("/:groupId/:start_generating/ai_image", auth.can("view group"), function (req, res) {
|
|
1651
|
-
|
|
1652
|
+
router.post("/:groupId/:start_generating/ai_image", auth.can("view group"), async function (req, res) {
|
|
1653
|
+
const imageOptions = normalizeImageGenerationProfileOptions(req.body.imageGenerationProfile, req.body.generationContext, req.body.imageType);
|
|
1654
|
+
if (imageOptions.error) {
|
|
1655
|
+
res.status(400).send({ error: imageOptions.error });
|
|
1656
|
+
return;
|
|
1657
|
+
}
|
|
1658
|
+
let guard;
|
|
1659
|
+
try {
|
|
1660
|
+
guard = await validateImageGenerationStartRequest(req, {
|
|
1661
|
+
collectionType: "group",
|
|
1662
|
+
collectionId: req.params.groupId,
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
catch (error) {
|
|
1666
|
+
log.error("Could not validate image generation request", {
|
|
1667
|
+
err: error,
|
|
1668
|
+
context: "start_generating_ai_image",
|
|
1669
|
+
user: req.user ? toJson(req.user.simple()) : null,
|
|
1670
|
+
});
|
|
1671
|
+
res.sendStatus(500);
|
|
1672
|
+
return;
|
|
1673
|
+
}
|
|
1674
|
+
if (!guard.allowed) {
|
|
1675
|
+
res.status(guard.status).send(guard.body);
|
|
1676
|
+
return;
|
|
1677
|
+
}
|
|
1678
|
+
const internalData = {
|
|
1679
|
+
...guard.internalData,
|
|
1680
|
+
imageGenerationProfile: imageOptions.imageGenerationProfile,
|
|
1681
|
+
};
|
|
1682
|
+
models.AcBackgroundJob.createJob({}, internalData, (error, jobId) => {
|
|
1652
1683
|
if (error) {
|
|
1653
1684
|
log.error("Could not create backgroundJob", {
|
|
1654
1685
|
err: error,
|
|
@@ -1660,13 +1691,18 @@ router.post("/:groupId/:start_generating/ai_image", auth.can("view group"), func
|
|
|
1660
1691
|
else {
|
|
1661
1692
|
queue.add("process-generative-ai", {
|
|
1662
1693
|
type: "collection-image",
|
|
1663
|
-
|
|
1664
|
-
userId: req.user ? req.user.id : 1,
|
|
1694
|
+
userId: guard.userId,
|
|
1665
1695
|
jobId: jobId,
|
|
1666
1696
|
collectionId: req.params.groupId,
|
|
1667
1697
|
collectionType: "group",
|
|
1668
1698
|
prompt: req.body.prompt,
|
|
1669
1699
|
imageType: req.body.imageType,
|
|
1700
|
+
generationContext: guard.generationContext,
|
|
1701
|
+
imageGenerationProfile: imageOptions.imageGenerationProfile,
|
|
1702
|
+
imageProvider: imageOptions.imageProvider,
|
|
1703
|
+
imageModel: imageOptions.imageModel,
|
|
1704
|
+
imageSize: imageOptions.imageSize,
|
|
1705
|
+
imageQuality: imageOptions.imageQuality,
|
|
1670
1706
|
}, "critical");
|
|
1671
1707
|
res.send({ jobId });
|
|
1672
1708
|
}
|
|
@@ -1674,14 +1710,23 @@ router.post("/:groupId/:start_generating/ai_image", auth.can("view group"), func
|
|
|
1674
1710
|
});
|
|
1675
1711
|
//TODO: Fix this permission back to edit
|
|
1676
1712
|
router.get("/:groupId/:jobId/poll_for_generating_ai_image", auth.can("view group"), function (req, res) {
|
|
1713
|
+
if (!req.user) {
|
|
1714
|
+
res.sendStatus(401);
|
|
1715
|
+
return;
|
|
1716
|
+
}
|
|
1677
1717
|
models.AcBackgroundJob.findOne({
|
|
1678
1718
|
where: {
|
|
1679
1719
|
id: req.params.jobId,
|
|
1680
1720
|
},
|
|
1681
|
-
attributes: ["id", "progress", "error", "data"],
|
|
1721
|
+
attributes: ["id", "progress", "error", "data", "internal_data"],
|
|
1682
1722
|
})
|
|
1683
1723
|
.then((job) => {
|
|
1684
|
-
|
|
1724
|
+
if (!canPollImageGenerationJob(req, job)) {
|
|
1725
|
+
res.sendStatus(404);
|
|
1726
|
+
}
|
|
1727
|
+
else {
|
|
1728
|
+
res.send(publicJobFields(job));
|
|
1729
|
+
}
|
|
1685
1730
|
})
|
|
1686
1731
|
.catch((error) => {
|
|
1687
1732
|
log.error("Could not get backgroundJob", {
|
package/controllers/points.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var async = require("async");
|
|
|
9
9
|
const ogs = require("open-graph-scraper");
|
|
10
10
|
var _ = require("lodash");
|
|
11
11
|
var queue = require("../services/workers/queue.cjs");
|
|
12
|
+
const { getFingerprintDataFromBody, } = require("../utils/fingerprint_data.cjs");
|
|
12
13
|
var changePointCounter = function (pointId, column, upDown, next) {
|
|
13
14
|
models.Point.findOne({
|
|
14
15
|
where: { id: pointId },
|
|
@@ -685,9 +686,7 @@ router.post("/:groupId", auth.can("create point"), function (req, res) {
|
|
|
685
686
|
user_agent: req.useragent.source,
|
|
686
687
|
ip_address: req.clientIp,
|
|
687
688
|
data: {
|
|
688
|
-
|
|
689
|
-
browserFingerprint: req.body.pointValCode,
|
|
690
|
-
browserFingerprintConfidence: req.body.pointConf,
|
|
689
|
+
...getFingerprintDataFromBody(req.body, "point"),
|
|
691
690
|
originalQueryString: req.body.originalQueryString,
|
|
692
691
|
userLocale: req.body.userLocale,
|
|
693
692
|
userAutoTranslate: req.body.userAutoTranslate,
|
|
@@ -930,9 +929,7 @@ router.post("/:id/pointQuality", auth.can("vote on point"), function (req, res)
|
|
|
930
929
|
pointQuality.value = req.body.value;
|
|
931
930
|
pointQuality.status = "active";
|
|
932
931
|
pointQuality.set("data", {
|
|
933
|
-
|
|
934
|
-
browserFingerprint: req.body.qualityValCode,
|
|
935
|
-
browserFingerprintConfidence: req.body.qualityConf,
|
|
932
|
+
...getFingerprintDataFromBody(req.body, "quality"),
|
|
936
933
|
});
|
|
937
934
|
}
|
|
938
935
|
else {
|
|
@@ -941,9 +938,7 @@ router.post("/:id/pointQuality", auth.can("vote on point"), function (req, res)
|
|
|
941
938
|
value: req.body.value,
|
|
942
939
|
user_id: req.user.id,
|
|
943
940
|
data: {
|
|
944
|
-
|
|
945
|
-
browserFingerprint: req.body.qualityValCode,
|
|
946
|
-
browserFingerprintConfidence: req.body.qualityConf,
|
|
941
|
+
...getFingerprintDataFromBody(req.body, "quality"),
|
|
947
942
|
},
|
|
948
943
|
status: "active",
|
|
949
944
|
user_agent: req.useragent.source,
|
package/controllers/posts.cjs
CHANGED
|
@@ -12,6 +12,7 @@ const getAnonymousUser = require("../services/utils/get_anonymous_system_user.cj
|
|
|
12
12
|
const moment = require("moment");
|
|
13
13
|
const { plausibleStatsProxy, } = require("../services/engine/analytics/plausible/manager.cjs");
|
|
14
14
|
const { isValidDbId } = require("../utils/is_valid_db_id.cjs");
|
|
15
|
+
const { getFingerprintDataFromBody, } = require("../utils/fingerprint_data.cjs");
|
|
15
16
|
var changePostCounter = function (req, postId, column, upDown, next) {
|
|
16
17
|
models.Post.findOne({
|
|
17
18
|
where: { id: postId },
|
|
@@ -963,9 +964,7 @@ var truthValueFromBody = function (bodyParameter) {
|
|
|
963
964
|
var updatePostData = function (req, post) {
|
|
964
965
|
if (!post.data) {
|
|
965
966
|
post.set("data", {
|
|
966
|
-
|
|
967
|
-
browserFingerprint: req.body.postValCode,
|
|
968
|
-
browserFingerprintConfidence: req.body.postConf,
|
|
967
|
+
...getFingerprintDataFromBody(req.body, "post"),
|
|
969
968
|
originalQueryString: req.body.originalQueryString,
|
|
970
969
|
userLocale: req.body.userLocale,
|
|
971
970
|
userAutoTranslate: req.body.userAutoTranslate,
|
|
@@ -1135,6 +1134,9 @@ router.post("/:groupId", auth.can("create post"), async function (req, res) {
|
|
|
1135
1134
|
status: "active",
|
|
1136
1135
|
user_agent: req.useragent.source,
|
|
1137
1136
|
ip_address: req.clientIp,
|
|
1137
|
+
data: {
|
|
1138
|
+
...getFingerprintDataFromBody(req.body, "post"),
|
|
1139
|
+
},
|
|
1138
1140
|
})
|
|
1139
1141
|
.save()
|
|
1140
1142
|
.then(function (endorsement) {
|
|
@@ -1683,9 +1685,7 @@ router.post("/:id/endorse", auth.can("vote on post"), async function (req, res)
|
|
|
1683
1685
|
endorsement.value = req.body.value;
|
|
1684
1686
|
endorsement.status = "active";
|
|
1685
1687
|
endorsement.set("data", {
|
|
1686
|
-
|
|
1687
|
-
browserFingerprint: req.body.endorsementValCode,
|
|
1688
|
-
browserFingerprintConfidence: req.body.endorsementConf,
|
|
1688
|
+
...getFingerprintDataFromBody(req.body, "endorsement"),
|
|
1689
1689
|
});
|
|
1690
1690
|
}
|
|
1691
1691
|
else {
|
|
@@ -1694,9 +1694,7 @@ router.post("/:id/endorse", auth.can("vote on post"), async function (req, res)
|
|
|
1694
1694
|
value: req.body.value,
|
|
1695
1695
|
user_id: req.user.id,
|
|
1696
1696
|
data: {
|
|
1697
|
-
|
|
1698
|
-
browserFingerprint: req.body.endorsementValCode,
|
|
1699
|
-
browserFingerprintConfidence: req.body.endorsementConf,
|
|
1697
|
+
...getFingerprintDataFromBody(req.body, "endorsement"),
|
|
1700
1698
|
},
|
|
1701
1699
|
status: "active",
|
|
1702
1700
|
user_agent: req.useragent.source,
|
package/controllers/ratings.cjs
CHANGED
|
@@ -9,6 +9,7 @@ var async = require('async');
|
|
|
9
9
|
var _ = require('lodash');
|
|
10
10
|
var queue = require('../services/workers/queue.cjs');
|
|
11
11
|
const moment = require('moment');
|
|
12
|
+
const { getFingerprintDataFromBody, } = require("../utils/fingerprint_data.cjs");
|
|
12
13
|
router.post('/:post_id/:type_index', auth.can('rate post'), function (req, res) {
|
|
13
14
|
var post;
|
|
14
15
|
models.Rating.findOne({
|
|
@@ -30,9 +31,7 @@ router.post('/:post_id/:type_index', auth.can('rate post'), function (req, res)
|
|
|
30
31
|
rating.value = req.body.value;
|
|
31
32
|
post = rating.Post;
|
|
32
33
|
rating.set('data', {
|
|
33
|
-
|
|
34
|
-
browserFingerprint: req.body.ratingValCode,
|
|
35
|
-
browserFingerprintConfidence: req.body.ratingConf
|
|
34
|
+
...getFingerprintDataFromBody(req.body, "rating"),
|
|
36
35
|
});
|
|
37
36
|
}
|
|
38
37
|
else {
|
|
@@ -42,9 +41,7 @@ router.post('/:post_id/:type_index', auth.can('rate post'), function (req, res)
|
|
|
42
41
|
value: req.body.value,
|
|
43
42
|
user_id: req.user.id,
|
|
44
43
|
data: {
|
|
45
|
-
|
|
46
|
-
browserFingerprint: req.body.ratingValCode,
|
|
47
|
-
browserFingerprintConfidence: req.body.ratingConf
|
|
44
|
+
...getFingerprintDataFromBody(req.body, "rating"),
|
|
48
45
|
},
|
|
49
46
|
user_agent: req.useragent.source,
|
|
50
47
|
ip_address: req.clientIp
|
package/models/point.cjs
CHANGED
|
@@ -3,6 +3,7 @@ const async = require('async');
|
|
|
3
3
|
const queue = require('../services/workers/queue.cjs');
|
|
4
4
|
const log = require('../utils/logger.cjs');
|
|
5
5
|
const _ = require("lodash");
|
|
6
|
+
const { getFingerprintDataFromBody, } = require("../utils/fingerprint_data.cjs");
|
|
6
7
|
const findCommunityAndDomainForPointFromGroup = (sequelize, options, callback) => {
|
|
7
8
|
sequelize.models.Group.findOne({
|
|
8
9
|
where: {
|
|
@@ -349,9 +350,7 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
349
350
|
},
|
|
350
351
|
(seriesCallback) => {
|
|
351
352
|
options.data = {
|
|
352
|
-
|
|
353
|
-
browserFingerprint: req.body.pointValCode,
|
|
354
|
-
browserFingerprintConfidence: req.body.pointConf,
|
|
353
|
+
...getFingerprintDataFromBody(req.body, "point"),
|
|
355
354
|
originalQueryString: req.body.originalQueryString,
|
|
356
355
|
userLocale: req.body.userLocale,
|
|
357
356
|
userAutoTranslate: req.body.userAutoTranslate,
|
|
@@ -541,6 +540,35 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
541
540
|
options.status = 'published';
|
|
542
541
|
options.user_agent = req.useragent.source;
|
|
543
542
|
options.ip_address = req.clientIp;
|
|
543
|
+
const body = req.body || {};
|
|
544
|
+
const data = options.data ? { ...options.data } : {};
|
|
545
|
+
const fingerprintData = getFingerprintDataFromBody(body, "point");
|
|
546
|
+
const optionalDataKeys = [
|
|
547
|
+
"originalQueryString",
|
|
548
|
+
"userLocale",
|
|
549
|
+
"userAutoTranslate",
|
|
550
|
+
"referrer",
|
|
551
|
+
"url",
|
|
552
|
+
"screen_width"
|
|
553
|
+
];
|
|
554
|
+
if (fingerprintData.browserId) {
|
|
555
|
+
data.browserId = fingerprintData.browserId;
|
|
556
|
+
}
|
|
557
|
+
if (fingerprintData.browserFingerprint) {
|
|
558
|
+
data.browserFingerprint = fingerprintData.browserFingerprint;
|
|
559
|
+
}
|
|
560
|
+
if (fingerprintData.browserId ||
|
|
561
|
+
fingerprintData.browserFingerprint ||
|
|
562
|
+
body.pointConf !== undefined) {
|
|
563
|
+
data.browserFingerprintConfidence =
|
|
564
|
+
fingerprintData.browserFingerprintConfidence;
|
|
565
|
+
}
|
|
566
|
+
optionalDataKeys.forEach(key => {
|
|
567
|
+
if (body[key] !== undefined) {
|
|
568
|
+
data[key] = body[key];
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
options.data = data;
|
|
544
572
|
sequelize.models.Point.build(options).save().then((point) => {
|
|
545
573
|
options.point_id = point.id;
|
|
546
574
|
const pointRevision = sequelize.models.PointRevision.build(options);
|
package/models/post.cjs
CHANGED
|
@@ -3,6 +3,7 @@ const async = require("async");
|
|
|
3
3
|
const queue = require('../services/workers/queue.cjs');
|
|
4
4
|
const log = require('../utils/logger.cjs');
|
|
5
5
|
const _ = require('lodash');
|
|
6
|
+
const { getFingerprintDataFromBody, } = require("../utils/fingerprint_data.cjs");
|
|
6
7
|
module.exports = (sequelize, DataTypes) => {
|
|
7
8
|
const Post = sequelize.define("Post", {
|
|
8
9
|
content_type: { type: DataTypes.INTEGER, allowNull: false },
|
|
@@ -490,9 +491,7 @@ module.exports = (sequelize, DataTypes) => {
|
|
|
490
491
|
user_agent: req.useragent.source,
|
|
491
492
|
ip_address: req.clientIp,
|
|
492
493
|
data: {
|
|
493
|
-
|
|
494
|
-
browserFingerprint: req.body.pointValCode,
|
|
495
|
-
browserFingerprintConfidence: req.body.pointConf,
|
|
494
|
+
...getFingerprintDataFromBody(req.body, "point"),
|
|
496
495
|
originalQueryString: req.body.originalQueryString,
|
|
497
496
|
userLocale: req.body.userLocale,
|
|
498
497
|
userAutoTranslate: req.body.userAutoTranslate,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yrpri/api",
|
|
3
|
-
"version": "9.0.
|
|
3
|
+
"version": "9.0.233",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Bjarnason & Citizens Foundation",
|
|
6
6
|
"repository": {
|
|
@@ -18,18 +18,18 @@
|
|
|
18
18
|
"dependencies": {
|
|
19
19
|
"@airbrake/node": "^2.1.9",
|
|
20
20
|
"@aws-sdk/client-s3": "^3.1029.0",
|
|
21
|
-
"@google-cloud/aiplatform": "^6.
|
|
22
|
-
"@google-cloud/speech": "^7.3.
|
|
21
|
+
"@google-cloud/aiplatform": "^6.8.0",
|
|
22
|
+
"@google-cloud/speech": "^7.3.1",
|
|
23
23
|
"@google-cloud/storage": "^7.19.0",
|
|
24
|
-
"@google-cloud/translate": "^9.
|
|
24
|
+
"@google-cloud/translate": "^9.4.1",
|
|
25
25
|
"@google-cloud/vertexai": "^1.10.0",
|
|
26
|
-
"@google-cloud/vision": "^5.3.
|
|
26
|
+
"@google-cloud/vision": "^5.3.6",
|
|
27
27
|
"@node-saml/passport-saml": "^5.1.0",
|
|
28
|
-
"@policysynth/agents": "^1.3.
|
|
28
|
+
"@policysynth/agents": "^1.3.182",
|
|
29
29
|
"async": "^3.2.6",
|
|
30
30
|
"authorized": "^1.0.0",
|
|
31
31
|
"aws-sdk": "^2.1693.0",
|
|
32
|
-
"axios": "^1.
|
|
32
|
+
"axios": "^1.16.1",
|
|
33
33
|
"bcrypt": "^6.0.0",
|
|
34
34
|
"body-parser": "^2.2.2",
|
|
35
35
|
"bullmq": "^5.31.0",
|
|
@@ -44,32 +44,32 @@
|
|
|
44
44
|
"deep-equal": "^2.2.3",
|
|
45
45
|
"docx": "^9.6.1",
|
|
46
46
|
"download-file": "^0.1.5",
|
|
47
|
-
"ejs": "^5.0.
|
|
47
|
+
"ejs": "^5.0.2",
|
|
48
48
|
"exceljs": "^4.4.0",
|
|
49
49
|
"express": "^5.2.1",
|
|
50
|
-
"express-rate-limit": "^8.
|
|
50
|
+
"express-rate-limit": "^8.5.1",
|
|
51
51
|
"express-session": "git+https://github.com/rbjarnason/session.git#upgrade-21",
|
|
52
|
-
"express-useragent": "^2.1.
|
|
52
|
+
"express-useragent": "^2.1.1",
|
|
53
53
|
"farmhash": "4.0.1",
|
|
54
54
|
"form-data": "^4.0.4",
|
|
55
55
|
"html-to-docx": "^1.8.0",
|
|
56
|
-
"i18next": "^26.0
|
|
56
|
+
"i18next": "^26.1.0",
|
|
57
57
|
"i18next-fs-backend": "^2.6.5",
|
|
58
58
|
"image-size": "^2.0.2",
|
|
59
|
-
"isbot": "^5.1.
|
|
59
|
+
"isbot": "^5.1.40",
|
|
60
60
|
"iso-639-1": "^3.1.5",
|
|
61
61
|
"jsonrepair": "^3.12.0",
|
|
62
62
|
"knuth-shuffle-seeded": "^1.0.6",
|
|
63
63
|
"lodash": "^4.18.1",
|
|
64
|
-
"marked": "^18.0.
|
|
64
|
+
"marked": "^18.0.3",
|
|
65
65
|
"moment": "^2.30.1",
|
|
66
66
|
"morgan": "^1.10.1",
|
|
67
67
|
"multer": "^2.1.1",
|
|
68
68
|
"multer-s3": "^3.0.1",
|
|
69
|
-
"newrelic": "^13.
|
|
70
|
-
"nodemailer": "^8.0.
|
|
69
|
+
"newrelic": "^13.20.0",
|
|
70
|
+
"nodemailer": "^8.0.7",
|
|
71
71
|
"open-graph-scraper": "^6.11.0",
|
|
72
|
-
"openai": "^6.
|
|
72
|
+
"openai": "^6.36.0",
|
|
73
73
|
"passport": "^0.7.0",
|
|
74
74
|
"passport-facebook": "^3.0.0",
|
|
75
75
|
"passport-github": "^1.1.0",
|
|
@@ -80,16 +80,16 @@
|
|
|
80
80
|
"pug": "^3.0.4",
|
|
81
81
|
"randomstring": "^1.3.1",
|
|
82
82
|
"rate-limit-redis": "^4.3.1",
|
|
83
|
-
"redis": "^5.
|
|
83
|
+
"redis": "^5.12.1",
|
|
84
84
|
"replicate": "^1.4.0",
|
|
85
85
|
"request-ip": "^3.3.0",
|
|
86
86
|
"sanitize-filename": "^1.6.4",
|
|
87
87
|
"sequelize": "^6.37.7",
|
|
88
88
|
"sharp": "^0.34.5",
|
|
89
89
|
"sitemap": "^9.0.1",
|
|
90
|
-
"stripe": "^22.
|
|
90
|
+
"stripe": "^22.1.1",
|
|
91
91
|
"striptags": "^3.2.0",
|
|
92
|
-
"uuid": "^13.0.
|
|
92
|
+
"uuid": "^13.0.2",
|
|
93
93
|
"ws": "^8.18.2"
|
|
94
94
|
},
|
|
95
95
|
"devDependencies": {
|
|
@@ -100,17 +100,18 @@
|
|
|
100
100
|
"@types/bunyan-prettystream": "^0.1.35",
|
|
101
101
|
"@types/compression": "^1.8.1",
|
|
102
102
|
"@types/cors": "^2.8.19",
|
|
103
|
-
"@types/express": "
|
|
104
|
-
"@types/express-
|
|
103
|
+
"@types/express": "5.0.6",
|
|
104
|
+
"@types/express-serve-static-core": "5.0.7",
|
|
105
|
+
"@types/express-session": "^1.19.0",
|
|
105
106
|
"@types/express-useragent": "^1.0.5",
|
|
106
107
|
"@types/lodash": "^4.17.24",
|
|
107
108
|
"@types/morgan": "^1.9.10",
|
|
108
|
-
"@types/node": "^22.19.
|
|
109
|
+
"@types/node": "^22.19.19",
|
|
109
110
|
"@types/passport": "^1.0.17",
|
|
110
111
|
"@types/request-ip": "^0.0.41",
|
|
111
112
|
"@types/uuid": "^10.0.0",
|
|
112
113
|
"@types/ws": "^8.18.1",
|
|
113
|
-
"axios": "^1.
|
|
114
|
+
"axios": "^1.16.1",
|
|
114
115
|
"copyfiles": "^2.4.1",
|
|
115
116
|
"csv-parse": "^6.2.1",
|
|
116
117
|
"diacritics": "^1.3.0",
|
|
@@ -127,52 +128,16 @@
|
|
|
127
128
|
"yamljs": "^0.3.0"
|
|
128
129
|
},
|
|
129
130
|
"overrides": {
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
"follow-redirects": "1.16.0",
|
|
133
|
-
"protobufjs": "7.5.5",
|
|
134
|
-
"validator": "13.15.35",
|
|
135
|
-
"path-to-regexp": "8.4.2",
|
|
136
|
-
"picomatch": "2.3.2",
|
|
137
|
-
"dottie": "2.0.7",
|
|
138
|
-
"glob@6.0.4": {
|
|
139
|
-
"minimatch": {
|
|
140
|
-
"brace-expansion": "1.1.14"
|
|
141
|
-
}
|
|
131
|
+
"@types/express@5.0.6": {
|
|
132
|
+
"@types/express-serve-static-core": "5.0.7"
|
|
142
133
|
},
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
|
|
146
|
-
}
|
|
134
|
+
"@google-cloud/storage": {
|
|
135
|
+
"retry-request": "8.0.2",
|
|
136
|
+
"teeny-request": "10.1.2"
|
|
147
137
|
},
|
|
148
|
-
"
|
|
149
|
-
"
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
},
|
|
153
|
-
"copyfiles": {
|
|
154
|
-
"minimatch": {
|
|
155
|
-
".": "3.1.4",
|
|
156
|
-
"brace-expansion": "1.1.14"
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
"readdir-glob": {
|
|
160
|
-
"minimatch": {
|
|
161
|
-
".": "5.1.9",
|
|
162
|
-
"brace-expansion": "2.1.0"
|
|
163
|
-
}
|
|
164
|
-
},
|
|
165
|
-
"@types/request": {
|
|
166
|
-
"form-data": "2.5.4"
|
|
167
|
-
},
|
|
168
|
-
"graphql-request": {
|
|
169
|
-
"form-data": "3.0.4"
|
|
170
|
-
},
|
|
171
|
-
"prebuild-install": {
|
|
172
|
-
"tar-fs": "2.1.4"
|
|
173
|
-
},
|
|
174
|
-
"prebuildify": {
|
|
175
|
-
"tar-fs": "2.1.4"
|
|
138
|
+
"express-session": {
|
|
139
|
+
"cookie": "0.7.2",
|
|
140
|
+
"on-headers": "1.1.0"
|
|
176
141
|
}
|
|
177
142
|
},
|
|
178
143
|
"scripts": {
|
|
@@ -4,18 +4,21 @@ export declare class AiHelper {
|
|
|
4
4
|
openaiClient: OpenAI;
|
|
5
5
|
wsClientSocket: WebSocket | undefined;
|
|
6
6
|
modelName: string;
|
|
7
|
+
answerIdeasModelName: string;
|
|
8
|
+
analysisModelName: string;
|
|
7
9
|
maxTokens: number;
|
|
8
10
|
temperature: number;
|
|
9
11
|
cacheExpireTime: number;
|
|
10
12
|
redisClient?: any;
|
|
11
13
|
cacheKeyForFullResponse?: string;
|
|
14
|
+
usesMaxCompletionTokens(modelName: string): boolean;
|
|
12
15
|
constructor(wsClientSocket?: WebSocket | undefined);
|
|
13
16
|
moderationSystemPrompt: (instructions: string) => string;
|
|
14
17
|
moderationUserPrompt: (question: string, answer: string) => string;
|
|
15
18
|
getModerationResponse: (instructions: string, question: string, answerToModerate: string) => Promise<boolean>;
|
|
16
|
-
streamChatCompletions(messages: any[]): Promise<void>;
|
|
17
|
-
sendToClient(sender: string, message: string, type?: string): void;
|
|
18
|
-
streamWebSocketResponses(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk
|
|
19
|
+
streamChatCompletions(messages: any[], modelName?: string, uniqueToken?: string): Promise<void>;
|
|
20
|
+
sendToClient(sender: string, message: string, type?: string, uniqueToken?: string): void;
|
|
21
|
+
streamWebSocketResponses(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>, uniqueToken?: string): Promise<void>;
|
|
19
22
|
getAnswerIdeas(question: string, previousIdeas: string[] | null, firstMessage: string | null): Promise<string | null | undefined>;
|
|
20
|
-
getAiAnalysis(questionId: number, contextPrompt: string, answers: AoiChoiceData[], cacheKeyForFullResponse: string, redisClient: any, locale: string, topOrBottomIdeasText: string, typeOfAnalysisText: string): Promise<string | null | undefined>;
|
|
23
|
+
getAiAnalysis(questionId: number, contextPrompt: string, answers: AoiChoiceData[], cacheKeyForFullResponse: string, redisClient: any, locale: string, topOrBottomIdeasText: string, typeOfAnalysisText: string, uniqueToken?: string): Promise<string | null | undefined>;
|
|
21
24
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { OpenAI } from "openai";
|
|
2
2
|
import log from "../../../utils/loggerTs.js";
|
|
3
3
|
export class AiHelper {
|
|
4
|
+
usesMaxCompletionTokens(modelName) {
|
|
5
|
+
return modelName.startsWith("gpt-5");
|
|
6
|
+
}
|
|
4
7
|
constructor(wsClientSocket = undefined) {
|
|
5
8
|
this.modelName = "gpt-4o";
|
|
9
|
+
this.answerIdeasModelName = "gpt-5.3-chat-latest";
|
|
10
|
+
this.analysisModelName = "gpt-5.3-chat-latest";
|
|
6
11
|
this.maxTokens = 2048;
|
|
7
12
|
this.temperature = 0.7;
|
|
8
13
|
this.cacheExpireTime = 60 * 60;
|
|
@@ -53,31 +58,41 @@ Only output: PASSES or FAILS`;
|
|
|
53
58
|
});
|
|
54
59
|
this.wsClientSocket = wsClientSocket;
|
|
55
60
|
}
|
|
56
|
-
async streamChatCompletions(messages) {
|
|
57
|
-
const
|
|
58
|
-
model:
|
|
61
|
+
async streamChatCompletions(messages, modelName = this.modelName, uniqueToken) {
|
|
62
|
+
const requestParams = {
|
|
63
|
+
model: modelName,
|
|
59
64
|
messages,
|
|
60
|
-
max_tokens: this.maxTokens,
|
|
61
|
-
temperature: this.temperature,
|
|
62
65
|
stream: true,
|
|
63
|
-
}
|
|
64
|
-
|
|
66
|
+
};
|
|
67
|
+
if (this.usesMaxCompletionTokens(modelName)) {
|
|
68
|
+
requestParams.max_completion_tokens = this.maxTokens;
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
requestParams.max_tokens = this.maxTokens;
|
|
72
|
+
requestParams.temperature = this.temperature;
|
|
73
|
+
}
|
|
74
|
+
const stream = await this.openaiClient.chat.completions.create(requestParams);
|
|
75
|
+
await this.streamWebSocketResponses(stream, uniqueToken);
|
|
65
76
|
}
|
|
66
|
-
sendToClient(sender, message, type = "stream") {
|
|
77
|
+
sendToClient(sender, message, type = "stream", uniqueToken) {
|
|
67
78
|
this.wsClientSocket?.send(JSON.stringify({
|
|
68
79
|
sender,
|
|
69
80
|
type: type,
|
|
70
81
|
message,
|
|
82
|
+
uniqueToken,
|
|
71
83
|
}));
|
|
72
84
|
}
|
|
73
|
-
async streamWebSocketResponses(stream) {
|
|
85
|
+
async streamWebSocketResponses(stream, uniqueToken) {
|
|
74
86
|
return new Promise(async (resolve, reject) => {
|
|
75
|
-
this.sendToClient("
|
|
87
|
+
this.sendToClient("assistant", "", "start", uniqueToken);
|
|
76
88
|
try {
|
|
77
89
|
let botMessage = "";
|
|
78
90
|
for await (const part of stream) {
|
|
79
|
-
|
|
80
|
-
|
|
91
|
+
const content = part.choices[0].delta.content;
|
|
92
|
+
if (content) {
|
|
93
|
+
this.sendToClient("assistant", content, "stream", uniqueToken);
|
|
94
|
+
botMessage += content;
|
|
95
|
+
}
|
|
81
96
|
}
|
|
82
97
|
if (this.redisClient && this.cacheKeyForFullResponse) {
|
|
83
98
|
this.redisClient.set(this.cacheKeyForFullResponse, botMessage, "EX", this.cacheExpireTime);
|
|
@@ -85,11 +100,11 @@ Only output: PASSES or FAILS`;
|
|
|
85
100
|
}
|
|
86
101
|
catch (error) {
|
|
87
102
|
log.error(error);
|
|
88
|
-
this.sendToClient("
|
|
103
|
+
this.sendToClient("assistant", "There has been an error, please retry", "error", uniqueToken);
|
|
89
104
|
reject();
|
|
90
105
|
}
|
|
91
106
|
finally {
|
|
92
|
-
this.sendToClient("
|
|
107
|
+
this.sendToClient("assistant", "", "end", uniqueToken);
|
|
93
108
|
}
|
|
94
109
|
resolve();
|
|
95
110
|
});
|
|
@@ -132,16 +147,16 @@ Only output: PASSES or FAILS`;
|
|
|
132
147
|
content: `What are some possible answers to the question: ${question}\n\n${previewIdeasText}Answers:\n`,
|
|
133
148
|
},
|
|
134
149
|
];
|
|
135
|
-
await this.streamChatCompletions(messages);
|
|
150
|
+
await this.streamChatCompletions(messages, this.answerIdeasModelName);
|
|
136
151
|
}
|
|
137
152
|
}
|
|
138
153
|
catch (error) {
|
|
139
154
|
log.error("Error in getAnswerIdeas:", error);
|
|
140
|
-
this.sendToClient("
|
|
155
|
+
this.sendToClient("assistant", "There has been an error, please retry", "error");
|
|
141
156
|
return null;
|
|
142
157
|
}
|
|
143
158
|
}
|
|
144
|
-
async getAiAnalysis(questionId, contextPrompt, answers, cacheKeyForFullResponse, redisClient, locale, topOrBottomIdeasText, typeOfAnalysisText) {
|
|
159
|
+
async getAiAnalysis(questionId, contextPrompt, answers, cacheKeyForFullResponse, redisClient, locale, topOrBottomIdeasText, typeOfAnalysisText, uniqueToken) {
|
|
145
160
|
this.redisClient = redisClient;
|
|
146
161
|
this.cacheKeyForFullResponse = cacheKeyForFullResponse;
|
|
147
162
|
const basePrePrompt = `You are a highly competent text and ideas analysis AI.
|
|
@@ -194,12 +209,12 @@ Only output: PASSES or FAILS`;
|
|
|
194
209
|
Answers to analyse:\n${answersText}`,
|
|
195
210
|
},
|
|
196
211
|
];
|
|
197
|
-
this.streamChatCompletions(messages);
|
|
212
|
+
this.streamChatCompletions(messages, this.analysisModelName, uniqueToken);
|
|
198
213
|
}
|
|
199
214
|
}
|
|
200
215
|
catch (error) {
|
|
201
216
|
log.error("Error in getAiAnalysis:", error);
|
|
202
|
-
this.sendToClient("
|
|
217
|
+
this.sendToClient("assistant", "There has been an error, please retry", "error", uniqueToken);
|
|
203
218
|
}
|
|
204
219
|
}
|
|
205
220
|
}
|
|
@@ -3,7 +3,7 @@ import { WebSocket } from "ws";
|
|
|
3
3
|
import { YpBaseChatBot } from "../../llms/baseChatBot.js";
|
|
4
4
|
export declare class ExplainAnswersAssistant extends YpBaseChatBot {
|
|
5
5
|
openaiClient: OpenAI;
|
|
6
|
-
|
|
6
|
+
llmModel: string;
|
|
7
7
|
maxTokens: number;
|
|
8
8
|
temperature: number;
|
|
9
9
|
languageName: string;
|
|
@@ -13,11 +13,11 @@ export class ExplainAnswersAssistant extends YpBaseChatBot {
|
|
|
13
13
|
tls: tlsOptions,
|
|
14
14
|
});
|
|
15
15
|
super(wsClientId, wsClients, redisConnection, `${YpBaseChatBot.redisMemoryKeyPrefix}-${uuidv4()}-explain-answers-assistant`);
|
|
16
|
-
this.
|
|
16
|
+
this.llmModel = "gpt-5.3-chat-latest";
|
|
17
17
|
this.maxTokens = 4000;
|
|
18
18
|
this.temperature = 0.8;
|
|
19
19
|
this.explainConversation = async (chatLog) => {
|
|
20
|
-
this.setChatLog(chatLog);
|
|
20
|
+
await this.setChatLog(chatLog);
|
|
21
21
|
let messages = chatLog.map((message) => {
|
|
22
22
|
return {
|
|
23
23
|
role: message.sender,
|
|
@@ -29,13 +29,7 @@ export class ExplainAnswersAssistant extends YpBaseChatBot {
|
|
|
29
29
|
content: this.renderSystemPrompt(),
|
|
30
30
|
};
|
|
31
31
|
messages.unshift(systemMessage);
|
|
32
|
-
const stream = await this.openaiClient.chat.completions.create(
|
|
33
|
-
model: this.llmModel,
|
|
34
|
-
messages,
|
|
35
|
-
max_tokens: this.maxTokens,
|
|
36
|
-
temperature: this.temperature,
|
|
37
|
-
stream: true,
|
|
38
|
-
});
|
|
32
|
+
const stream = await this.openaiClient.chat.completions.create(this.getStreamingChatCompletionParams(messages));
|
|
39
33
|
this.streamWebSocketResponses(stream);
|
|
40
34
|
};
|
|
41
35
|
this.languageName = languageName;
|