@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.
Files changed (68) hide show
  1. package/controllers/allOurIdeas.js +1 -1
  2. package/controllers/communities.cjs +282 -91
  3. package/controllers/domains.cjs +54 -8
  4. package/controllers/groups.cjs +51 -6
  5. package/controllers/points.cjs +4 -9
  6. package/controllers/posts.cjs +7 -9
  7. package/controllers/ratings.cjs +3 -6
  8. package/models/point.cjs +31 -3
  9. package/models/post.cjs +2 -3
  10. package/package.json +2 -2
  11. package/services/engine/allOurIdeas/aiHelper.d.ts +7 -4
  12. package/services/engine/allOurIdeas/aiHelper.js +34 -19
  13. package/services/engine/allOurIdeas/explainAnswersAssistant.d.ts +1 -1
  14. package/services/engine/allOurIdeas/explainAnswersAssistant.js +3 -9
  15. package/services/engine/moderation/fraud/CreateFraudAuditReport.cjs +35 -11
  16. package/services/engine/moderation/fraud/CreateFraudAuditReport.d.cts +21 -0
  17. package/services/engine/moderation/fraud/FraudBase.cjs +38 -18
  18. package/services/engine/moderation/fraud/FraudBase.d.cts +2 -0
  19. package/services/engine/moderation/fraud/FraudDeleteBase.cjs +48 -29
  20. package/services/engine/moderation/fraud/FraudDeleteBase.d.cts +8 -6
  21. package/services/engine/moderation/fraud/FraudDeleteEndorsements.cjs +5 -4
  22. package/services/engine/moderation/fraud/FraudDeleteEndorsements.d.cts +2 -2
  23. package/services/engine/moderation/fraud/FraudDeletePointQualities.cjs +3 -2
  24. package/services/engine/moderation/fraud/FraudDeletePointQualities.d.cts +1 -1
  25. package/services/engine/moderation/fraud/FraudDeletePoints.cjs +3 -2
  26. package/services/engine/moderation/fraud/FraudDeletePoints.d.cts +1 -1
  27. package/services/engine/moderation/fraud/FraudDeletePosts.cjs +3 -2
  28. package/services/engine/moderation/fraud/FraudDeleteRatings.cjs +61 -4
  29. package/services/engine/moderation/fraud/FraudGetBase.cjs +44 -20
  30. package/services/engine/moderation/fraud/FraudGetBase.d.cts +5 -0
  31. package/services/engine/moderation/fraud/FraudGetEndorsements.cjs +4 -13
  32. package/services/engine/moderation/fraud/FraudGetEndorsements.d.cts +1 -1
  33. package/services/engine/moderation/fraud/FraudGetPointQualities.cjs +3 -0
  34. package/services/engine/moderation/fraud/FraudGetPointQualities.d.cts +1 -1
  35. package/services/engine/moderation/fraud/FraudGetPoints.cjs +3 -0
  36. package/services/engine/moderation/fraud/FraudGetPoints.d.cts +1 -1
  37. package/services/engine/moderation/fraud/FraudGetPosts.cjs +17 -16
  38. package/services/engine/moderation/fraud/FraudGetPosts.d.cts +3 -3
  39. package/services/engine/moderation/fraud/FraudGetRatings.cjs +62 -30
  40. package/services/engine/moderation/fraud/FraudGetRatings.d.cts +4 -1
  41. package/services/engine/moderation/fraud/FraudRequestValidation.cjs +143 -0
  42. package/services/engine/moderation/fraud/FraudRequestValidation.d.cts +21 -0
  43. package/services/engine/moderation/fraud/FraudScannerNotifier.cjs +59 -35
  44. package/services/engine/moderation/fraud/FraudScannerNotifier.d.cts +20 -1
  45. package/services/llms/baseChatBot.d.ts +2 -0
  46. package/services/llms/baseChatBot.js +25 -9
  47. package/services/llms/imageGeneration/chatGptImageGenerator.d.ts +2 -2
  48. package/services/llms/imageGeneration/chatGptImageGenerator.js +13 -10
  49. package/services/llms/imageGeneration/collectionImageGenerator.js +31 -13
  50. package/services/llms/imageGeneration/dalleImageGenerator.d.ts +2 -2
  51. package/services/llms/imageGeneration/dalleImageGenerator.js +28 -16
  52. package/services/llms/imageGeneration/fluxImageGenerator.d.ts +2 -2
  53. package/services/llms/imageGeneration/fluxImageGenerator.js +9 -3
  54. package/services/llms/imageGeneration/iImageGenerator.d.ts +8 -1
  55. package/services/llms/imageGeneration/imageModelConfig.cjs +319 -0
  56. package/services/llms/imageGeneration/imageModelConfig.d.cts +79 -0
  57. package/services/llms/imageGeneration/imagenImageGenerator.d.ts +2 -3
  58. package/services/llms/imageGeneration/imagenImageGenerator.js +10 -10
  59. package/tests/fraudManagement.test.cjs +470 -0
  60. package/tests/fraudManagement.test.d.cts +1 -0
  61. package/tests/imageModelConfig.test.cjs +144 -0
  62. package/tests/imageModelConfig.test.d.cts +1 -0
  63. package/utils/ai_image_generation_guard.cjs +268 -0
  64. package/utils/ai_image_generation_guard.d.cts +34 -0
  65. package/utils/fingerprint_data.cjs +32 -0
  66. package/utils/fingerprint_data.d.cts +6 -0
  67. package/utils/recount_utils.cjs +53 -37
  68. package/utils/recount_utils.d.cts +7 -7
@@ -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
- models.AcBackgroundJob.createJob({}, {}, (error, jobId) => {
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
- //TODO: Look into this
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
- res.send(job);
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", {
@@ -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
- browserId: req.body.pointBaseId,
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
- browserId: req.body.qualityBaseId,
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
- browserId: req.body.qualityBaseId,
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,
@@ -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
- browserId: req.body.postBaseId,
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
- browserId: req.body.endorsementBaseId,
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
- browserId: req.body.endorsementBaseId,
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,
@@ -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
- browserId: req.body.ratingBaseId,
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
- browserId: req.body.ratingBaseId,
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
- browserId: req.body.pointBaseId,
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
- browserId: req.body.pointBaseId,
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.231",
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.34.0",
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>): Promise<void>;
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 stream = await this.openaiClient.chat.completions.create({
58
- model: this.modelName,
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
- await this.streamWebSocketResponses(stream);
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("bot", "", "start");
87
+ this.sendToClient("assistant", "", "start", uniqueToken);
76
88
  try {
77
89
  let botMessage = "";
78
90
  for await (const part of stream) {
79
- this.sendToClient("bot", part.choices[0].delta.content);
80
- botMessage += part.choices[0].delta.content;
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("bot", "There has been an error, please retry", "error");
103
+ this.sendToClient("assistant", "There has been an error, please retry", "error", uniqueToken);
89
104
  reject();
90
105
  }
91
106
  finally {
92
- this.sendToClient("bot", "", "end");
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("bot", "There has been an error, please retry", "error");
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("bot", "There has been an error, please retry", "error");
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
- modelName: string;
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.modelName = "gpt-4o";
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 model.unscoped().findAll({
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: "postId", width: 20 },
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
- ".xls";
180
- this.workPackage.fileEnding = "xls";
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");