@yrpri/api 9.0.231 → 9.0.232
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/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 +2 -2
- 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.232",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Robert Bjarnason & Citizens Foundation",
|
|
6
6
|
"repository": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"newrelic": "^13.18.0",
|
|
70
70
|
"nodemailer": "^8.0.5",
|
|
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",
|
|
@@ -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;
|
|
@@ -11,6 +11,16 @@ const formatWorksheet = (worksheet) => {
|
|
|
11
11
|
worksheet.getRow(1).font = { bold: true };
|
|
12
12
|
// worksheet.properties.defaultRowHeight = 20;
|
|
13
13
|
};
|
|
14
|
+
const sanitizeWorksheetName = (name) => {
|
|
15
|
+
const sanitizedName = String(name || "Fraud Audit")
|
|
16
|
+
.replace(/[\\/\*\?:\[\]]/g, " ")
|
|
17
|
+
.replace(/\s+/g, " ")
|
|
18
|
+
.trim()
|
|
19
|
+
.substring(0, 31)
|
|
20
|
+
.trim()
|
|
21
|
+
.replace(/^'+|'+$/g, "");
|
|
22
|
+
return sanitizedName.trim() || "Fraud Audit";
|
|
23
|
+
};
|
|
14
24
|
class FraudAuditReport {
|
|
15
25
|
constructor(workPackage) {
|
|
16
26
|
this.workPackage = workPackage;
|
|
@@ -100,7 +110,7 @@ class FraudAuditReport {
|
|
|
100
110
|
return items;
|
|
101
111
|
}
|
|
102
112
|
async getPostItems(ids) {
|
|
103
|
-
return await
|
|
113
|
+
return await models.Post.unscoped().findAll({
|
|
104
114
|
attributes: ["id", "created_at", "group_id", "user_id", "user_agent", "ip_address", "data"],
|
|
105
115
|
where: {
|
|
106
116
|
id: {
|
|
@@ -133,7 +143,7 @@ class FraudAuditReport {
|
|
|
133
143
|
this.workBook = new Excel.Workbook();
|
|
134
144
|
this.workBook.creator = "Your Priorities Report - Automated";
|
|
135
145
|
this.workBook.created = new Date();
|
|
136
|
-
this.worksheet = this.workBook.addWorksheet(`Community Users ${this.workPackage.community.id} ${this.workPackage.userName}`);
|
|
146
|
+
this.worksheet = this.workBook.addWorksheet(sanitizeWorksheetName(`Community Users ${this.workPackage.community.id} ${this.workPackage.userName}`));
|
|
137
147
|
this.worksheet.columns = [
|
|
138
148
|
{ header: "Type", key: "collectionType", width: 20 },
|
|
139
149
|
{ header: "Method", key: "selectedMethod", width: 20 },
|
|
@@ -159,7 +169,7 @@ class FraudAuditReport {
|
|
|
159
169
|
}
|
|
160
170
|
if (['pointQualities'].indexOf(originalWorkPackage.collectionType) !== -1) {
|
|
161
171
|
this.worksheet.columns = this.worksheet.columns.concat([
|
|
162
|
-
{ header: "Point Id", key: "
|
|
172
|
+
{ header: "Point Id", key: "pointId", width: 20 },
|
|
163
173
|
]);
|
|
164
174
|
}
|
|
165
175
|
this.worksheet.columns = this.worksheet.columns.concat([
|
|
@@ -176,8 +186,8 @@ class FraudAuditReport {
|
|
|
176
186
|
communityName +
|
|
177
187
|
"_" +
|
|
178
188
|
dateString +
|
|
179
|
-
".
|
|
180
|
-
this.workPackage.fileEnding = "
|
|
189
|
+
".xlsx";
|
|
190
|
+
this.workPackage.fileEnding = "xlsx";
|
|
181
191
|
}
|
|
182
192
|
async getCommunity() {
|
|
183
193
|
this.workPackage.community = await models.Community.findOne({
|
|
@@ -240,8 +250,8 @@ class FraudAuditReport {
|
|
|
240
250
|
dateDeleted: this.workPackage.auditReportData.date,
|
|
241
251
|
id: item.id,
|
|
242
252
|
date: item.created_at,
|
|
243
|
-
userId: item.User.id,
|
|
244
|
-
email: item.User.email,
|
|
253
|
+
userId: item.User ? item.User.id : item.user_id,
|
|
254
|
+
email: item.User ? item.User.email : "",
|
|
245
255
|
ipAddress: item.ip_address,
|
|
246
256
|
userAgent: item.user_agent,
|
|
247
257
|
};
|
|
@@ -250,14 +260,14 @@ class FraudAuditReport {
|
|
|
250
260
|
row.browserId = item.data.browserId;
|
|
251
261
|
}
|
|
252
262
|
if (['posts', 'pointQualities'].indexOf(originalWorkPackage.collectionType) === -1) {
|
|
253
|
-
row.postId = item.Post.id;
|
|
254
|
-
row.postName = item.Post.name;
|
|
263
|
+
row.postId = item.Post ? item.Post.id : item.post_id;
|
|
264
|
+
row.postName = item.Post ? item.Post.name : "";
|
|
255
265
|
}
|
|
256
266
|
if (['endorsements', 'pointQualities', 'ratings'].indexOf(originalWorkPackage.collectionType) !== -1) {
|
|
257
267
|
row.value = item.value;
|
|
258
268
|
}
|
|
259
269
|
if (['pointQualities'].indexOf(originalWorkPackage.collectionType) !== -1) {
|
|
260
|
-
row.pointId = item.Point.id;
|
|
270
|
+
row.pointId = item.Point ? item.Point.id : item.point_id;
|
|
261
271
|
}
|
|
262
272
|
this.worksheet.addRow(row);
|
|
263
273
|
}
|
|
@@ -272,6 +282,7 @@ class FraudAuditReport {
|
|
|
272
282
|
}
|
|
273
283
|
});
|
|
274
284
|
if (auditReport && auditReport.data) {
|
|
285
|
+
this.validateAuditReportCommunity(auditReport);
|
|
275
286
|
this.workPackage.auditReportData = auditReport.data;
|
|
276
287
|
await this.getCommunity();
|
|
277
288
|
this.setupFilename();
|
|
@@ -296,6 +307,17 @@ class FraudAuditReport {
|
|
|
296
307
|
}
|
|
297
308
|
});
|
|
298
309
|
}
|
|
310
|
+
validateAuditReportCommunity(auditReport) {
|
|
311
|
+
const auditCommunityId = auditReport &&
|
|
312
|
+
auditReport.data &&
|
|
313
|
+
auditReport.data.workPackage &&
|
|
314
|
+
auditReport.data.workPackage.communityId;
|
|
315
|
+
if (auditCommunityId === undefined ||
|
|
316
|
+
auditCommunityId === null ||
|
|
317
|
+
String(auditCommunityId) !== String(this.workPackage.communityId)) {
|
|
318
|
+
throw new Error("Fraud audit report does not belong to this community");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
299
321
|
}
|
|
300
322
|
const createFraudAuditReport = async (workPackage, done) => {
|
|
301
323
|
return await new Promise(async (resolve, reject) => {
|
|
@@ -310,5 +332,7 @@ const createFraudAuditReport = async (workPackage, done) => {
|
|
|
310
332
|
});
|
|
311
333
|
};
|
|
312
334
|
module.exports = {
|
|
313
|
-
createFraudAuditReport
|
|
335
|
+
createFraudAuditReport,
|
|
336
|
+
FraudAuditReport,
|
|
337
|
+
sanitizeWorksheetName
|
|
314
338
|
};
|
|
@@ -1 +1,22 @@
|
|
|
1
1
|
export function createFraudAuditReport(workPackage: any, done: any): Promise<any>;
|
|
2
|
+
export class FraudAuditReport {
|
|
3
|
+
constructor(workPackage: any);
|
|
4
|
+
workPackage: any;
|
|
5
|
+
exportedData: Excel.Buffer | null;
|
|
6
|
+
items: any;
|
|
7
|
+
workBook: Excel.Workbook | null;
|
|
8
|
+
worksheet: Excel.Worksheet | null;
|
|
9
|
+
getPointQualityItems(ids: any): Promise<(import("sequelize").Model<YpPointQuality, Partial<YpPointQuality>> & YpPointQuality)[]>;
|
|
10
|
+
getPostDependedItems(model: any, ids: any): Promise<any>;
|
|
11
|
+
getPostItems(ids: any): Promise<(import("sequelize").Model<YpPostData, Partial<YpPostData>> & YpPostData)[]>;
|
|
12
|
+
setupXls(): Promise<void>;
|
|
13
|
+
setupFilename(): void;
|
|
14
|
+
getCommunity(): Promise<void>;
|
|
15
|
+
uploadToS3(): Promise<any>;
|
|
16
|
+
setupItems(): Promise<void>;
|
|
17
|
+
populateXls(): Promise<void>;
|
|
18
|
+
createReport(): Promise<any>;
|
|
19
|
+
validateAuditReportCommunity(auditReport: any): void;
|
|
20
|
+
}
|
|
21
|
+
export function sanitizeWorksheetName(name: any): string;
|
|
22
|
+
import Excel = require("exceljs");
|