@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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const MAX_FRAUD_IDS_TO_DELETE = 1000;
|
|
3
|
+
const validActionTypes = new Set(["get-items", "delete-one-item", "delete-items"]);
|
|
4
|
+
const validCollectionTypes = new Set([
|
|
5
|
+
"endorsements",
|
|
6
|
+
"ratings",
|
|
7
|
+
"pointQualities",
|
|
8
|
+
"points",
|
|
9
|
+
"posts",
|
|
10
|
+
]);
|
|
11
|
+
const commonMethods = new Set([
|
|
12
|
+
"byIpFingerprint",
|
|
13
|
+
"byMissingBrowserFingerprint",
|
|
14
|
+
"byIpAddress",
|
|
15
|
+
]);
|
|
16
|
+
const postMethods = new Set([
|
|
17
|
+
...commonMethods,
|
|
18
|
+
"byIpFingerprintPostId",
|
|
19
|
+
"byIpUserAgentPostId",
|
|
20
|
+
]);
|
|
21
|
+
const pointMethods = new Set([
|
|
22
|
+
...commonMethods,
|
|
23
|
+
"byIpFingerprintPointId",
|
|
24
|
+
"byIpUserAgentPointId",
|
|
25
|
+
]);
|
|
26
|
+
const normalizeIdValue = (value) => {
|
|
27
|
+
if (typeof value === "number") {
|
|
28
|
+
return Number.isSafeInteger(value) && value > 0 ? value : null;
|
|
29
|
+
}
|
|
30
|
+
else if (typeof value === "string") {
|
|
31
|
+
const trimmedValue = value.trim();
|
|
32
|
+
if (!/^[1-9]\d*$/.test(trimmedValue)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
const parsedValue = Number(trimmedValue);
|
|
36
|
+
return Number.isSafeInteger(parsedValue) ? parsedValue : null;
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
const validateIdsToDelete = (idsToDelete) => {
|
|
43
|
+
if (idsToDelete === undefined || idsToDelete === null) {
|
|
44
|
+
return { idsToDelete: [] };
|
|
45
|
+
}
|
|
46
|
+
if (!Array.isArray(idsToDelete)) {
|
|
47
|
+
return {
|
|
48
|
+
error: "invalid_ids_to_delete",
|
|
49
|
+
idsToDelete: [],
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
if (idsToDelete.length > MAX_FRAUD_IDS_TO_DELETE) {
|
|
53
|
+
return {
|
|
54
|
+
error: "too_many_ids_to_delete",
|
|
55
|
+
idsToDelete: [],
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const seenIds = new Set();
|
|
59
|
+
const normalizedIds = [];
|
|
60
|
+
for (let i = 0; i < idsToDelete.length; i++) {
|
|
61
|
+
const normalizedId = normalizeIdValue(idsToDelete[i]);
|
|
62
|
+
if (!normalizedId) {
|
|
63
|
+
return {
|
|
64
|
+
error: "invalid_ids_to_delete",
|
|
65
|
+
idsToDelete: [],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
if (!seenIds.has(normalizedId)) {
|
|
69
|
+
seenIds.add(normalizedId);
|
|
70
|
+
normalizedIds.push(normalizedId);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return { idsToDelete: normalizedIds };
|
|
74
|
+
};
|
|
75
|
+
const normalizeIdsToDelete = (idsToDelete) => {
|
|
76
|
+
return validateIdsToDelete(idsToDelete).idsToDelete;
|
|
77
|
+
};
|
|
78
|
+
const getValidMethodsForCollectionType = (collectionType) => {
|
|
79
|
+
if (collectionType === "pointQualities") {
|
|
80
|
+
return pointMethods;
|
|
81
|
+
}
|
|
82
|
+
else if (collectionType === "posts") {
|
|
83
|
+
return commonMethods;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
return postMethods;
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
const validateFraudActionRequest = ({ type, selectedMethod, collectionType, idsToDelete, }) => {
|
|
90
|
+
if (!validActionTypes.has(type)) {
|
|
91
|
+
return {
|
|
92
|
+
error: "invalid_fraud_action_type",
|
|
93
|
+
idsToDelete: [],
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
if (!validCollectionTypes.has(collectionType)) {
|
|
97
|
+
return {
|
|
98
|
+
error: "invalid_fraud_collection_type",
|
|
99
|
+
idsToDelete: [],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
const validMethods = getValidMethodsForCollectionType(collectionType);
|
|
103
|
+
if (!validMethods.has(selectedMethod)) {
|
|
104
|
+
return {
|
|
105
|
+
error: "invalid_fraud_detection_method",
|
|
106
|
+
idsToDelete: [],
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
const idsValidation = validateIdsToDelete(idsToDelete);
|
|
110
|
+
const normalizedIdsToDelete = idsValidation.idsToDelete;
|
|
111
|
+
if (idsValidation.error) {
|
|
112
|
+
return idsValidation;
|
|
113
|
+
}
|
|
114
|
+
if ((type === "delete-one-item" || type === "delete-items") &&
|
|
115
|
+
normalizedIdsToDelete.length === 0) {
|
|
116
|
+
return {
|
|
117
|
+
error: "delete_requires_ids",
|
|
118
|
+
idsToDelete: normalizedIdsToDelete,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
if (type === "delete-one-item" && normalizedIdsToDelete.length !== 1) {
|
|
122
|
+
return {
|
|
123
|
+
error: "single_delete_requires_one_id",
|
|
124
|
+
idsToDelete: normalizedIdsToDelete,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
if (selectedMethod === "byMissingBrowserFingerprint" &&
|
|
128
|
+
(type === "delete-items" || normalizedIdsToDelete.length > 1)) {
|
|
129
|
+
return {
|
|
130
|
+
error: "bulk_delete_missing_fingerprint_disabled",
|
|
131
|
+
idsToDelete: normalizedIdsToDelete,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
idsToDelete: normalizedIdsToDelete,
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
module.exports = {
|
|
139
|
+
MAX_FRAUD_IDS_TO_DELETE,
|
|
140
|
+
normalizeIdsToDelete,
|
|
141
|
+
validateFraudActionRequest,
|
|
142
|
+
validateIdsToDelete,
|
|
143
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export const MAX_FRAUD_IDS_TO_DELETE: 1000;
|
|
2
|
+
export function normalizeIdsToDelete(idsToDelete: any): number[] | never[];
|
|
3
|
+
export function validateFraudActionRequest({ type, selectedMethod, collectionType, idsToDelete, }: {
|
|
4
|
+
type: any;
|
|
5
|
+
selectedMethod: any;
|
|
6
|
+
collectionType: any;
|
|
7
|
+
idsToDelete: any;
|
|
8
|
+
}): {
|
|
9
|
+
error: string;
|
|
10
|
+
idsToDelete: number[] | never[];
|
|
11
|
+
} | {
|
|
12
|
+
idsToDelete: number[] | never[];
|
|
13
|
+
error?: undefined;
|
|
14
|
+
};
|
|
15
|
+
export function validateIdsToDelete(idsToDelete: any): {
|
|
16
|
+
error: string;
|
|
17
|
+
idsToDelete: never[];
|
|
18
|
+
} | {
|
|
19
|
+
idsToDelete: number[];
|
|
20
|
+
error?: undefined;
|
|
21
|
+
};
|
|
@@ -4,9 +4,11 @@ const models = require("../../../../models/index.cjs");
|
|
|
4
4
|
const i18n = require('../../../utils/i18n.cjs');
|
|
5
5
|
const deepEqual = require('deep-equal');
|
|
6
6
|
const log = require("../../../../utils/logger.cjs");
|
|
7
|
-
const FraudGetEndorsements = require("./FraudGetEndorsements");
|
|
8
|
-
const FraudGetPointQualities = require("./FraudGetPointQualities");
|
|
9
|
-
const FraudGetRatings = require("./FraudGetRatings");
|
|
7
|
+
const FraudGetEndorsements = require("./FraudGetEndorsements.cjs");
|
|
8
|
+
const FraudGetPointQualities = require("./FraudGetPointQualities.cjs");
|
|
9
|
+
const FraudGetRatings = require("./FraudGetRatings.cjs");
|
|
10
|
+
const FraudGetPoints = require("./FraudGetPoints.cjs");
|
|
11
|
+
const FraudGetPosts = require("./FraudGetPosts.cjs");
|
|
10
12
|
const queue = require("../../../workers/queue.cjs");
|
|
11
13
|
const Backend = require("i18next-fs-backend");
|
|
12
14
|
const path = require("path");
|
|
@@ -15,8 +17,17 @@ class FraudScannerNotifier {
|
|
|
15
17
|
constructor() {
|
|
16
18
|
this.currentCommunity = null;
|
|
17
19
|
this.uniqueCollectionItemsIds = {};
|
|
18
|
-
this.collectionsToScan = ['endorsements', 'ratings', 'pointQualities'];
|
|
19
|
-
this.scannerModels = [
|
|
20
|
+
this.collectionsToScan = ['endorsements', 'ratings', 'pointQualities', 'points', 'posts'];
|
|
21
|
+
this.scannerModels = [
|
|
22
|
+
FraudGetEndorsements,
|
|
23
|
+
FraudGetRatings,
|
|
24
|
+
FraudGetPointQualities,
|
|
25
|
+
FraudGetPoints,
|
|
26
|
+
FraudGetPosts
|
|
27
|
+
];
|
|
28
|
+
}
|
|
29
|
+
resetCounts() {
|
|
30
|
+
this.uniqueCollectionItemsIds = {};
|
|
20
31
|
}
|
|
21
32
|
getCommunityURL() {
|
|
22
33
|
if (this.currentCommunity && this.currentCommunity.Domain && this.currentCommunity.Domain.domain_name) {
|
|
@@ -134,7 +145,7 @@ class FraudScannerNotifier {
|
|
|
134
145
|
}
|
|
135
146
|
getContainerOldCount(collectionType) {
|
|
136
147
|
let foundCollection;
|
|
137
|
-
if (this.currentCommunity.data.lastFraudScanResults) {
|
|
148
|
+
if (this.currentCommunity.data && this.currentCommunity.data.lastFraudScanResults) {
|
|
138
149
|
for (let i = 0; i < this.currentCommunity.data.lastFraudScanResults.length; i++) {
|
|
139
150
|
if (this.currentCommunity.data.lastFraudScanResults[i].collectionType &&
|
|
140
151
|
this.currentCommunity.data.lastFraudScanResults[i].collectionType === collectionType) {
|
|
@@ -187,7 +198,7 @@ class FraudScannerNotifier {
|
|
|
187
198
|
if (this.collectionsToScan[c] === "pointQualities") {
|
|
188
199
|
methodsToScan = methodsToScan.concat(['byIpFingerprintPointId', 'byIpUserAgentPointId']);
|
|
189
200
|
}
|
|
190
|
-
else {
|
|
201
|
+
else if (this.collectionsToScan[c] !== "posts") {
|
|
191
202
|
methodsToScan = methodsToScan.concat(['byIpFingerprintPostId', 'byIpUserAgentPostId']);
|
|
192
203
|
}
|
|
193
204
|
for (let m = 0; m < methodsToScan.length; m++) {
|
|
@@ -230,6 +241,7 @@ class FraudScannerNotifier {
|
|
|
230
241
|
for (let i = 0; i < communities.length; i++) {
|
|
231
242
|
log.info("Processing community: " + communities[i].name);
|
|
232
243
|
this.currentCommunity = communities[i];
|
|
244
|
+
this.resetCounts();
|
|
233
245
|
try {
|
|
234
246
|
await this.scan();
|
|
235
247
|
await this.notify();
|
|
@@ -249,32 +261,44 @@ class FraudScannerNotifier {
|
|
|
249
261
|
});
|
|
250
262
|
}
|
|
251
263
|
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
'
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
}, function (err, t) {
|
|
268
|
-
(async () => {
|
|
269
|
-
try {
|
|
270
|
-
const scanner = new FraudScannerNotifier();
|
|
271
|
-
await scanner.scanAndNotify();
|
|
272
|
-
log.info("Fraud Scanning Complete");
|
|
273
|
-
process.exit();
|
|
274
|
-
}
|
|
275
|
-
catch (error) {
|
|
276
|
-
log.error(error);
|
|
277
|
-
process.exit();
|
|
264
|
+
const runFraudScannerNotifier = () => {
|
|
265
|
+
i18n
|
|
266
|
+
.use(Backend)
|
|
267
|
+
.init({
|
|
268
|
+
preload: ['en', 'fr', 'sk', 'bg', 'cs', 'it', 'da', 'kl', 'es', 'sv', 'sq', 'uz', 'uk', 'ca', 'hr', 'ro', 'ru',
|
|
269
|
+
'ro_md', 'pt_br', 'hu', 'tr', 'is', 'nl', 'no', 'pl', 'zh_tw', 'ky'],
|
|
270
|
+
fallbackLng: 'en',
|
|
271
|
+
// this is the defaults
|
|
272
|
+
backend: {
|
|
273
|
+
// path where resources get loaded from
|
|
274
|
+
loadPath: localesPath + '/{{lng}}/translation.json',
|
|
275
|
+
// path to post missing resources
|
|
276
|
+
addPath: localesPath + '/{{lng}}/translation.missing.json',
|
|
277
|
+
// jsonIndent to use when storing json files
|
|
278
|
+
jsonIndent: 2
|
|
278
279
|
}
|
|
279
|
-
}
|
|
280
|
-
|
|
280
|
+
}, function (err, t) {
|
|
281
|
+
(async () => {
|
|
282
|
+
try {
|
|
283
|
+
if (err) {
|
|
284
|
+
throw err;
|
|
285
|
+
}
|
|
286
|
+
const scanner = new FraudScannerNotifier();
|
|
287
|
+
await scanner.scanAndNotify();
|
|
288
|
+
log.info("Fraud Scanning Complete");
|
|
289
|
+
process.exit(0);
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
log.error(error);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
})();
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
if (require.main === module) {
|
|
299
|
+
runFraudScannerNotifier();
|
|
300
|
+
}
|
|
301
|
+
module.exports = {
|
|
302
|
+
FraudScannerNotifier,
|
|
303
|
+
runFraudScannerNotifier
|
|
304
|
+
};
|
|
@@ -1 +1,20 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export class FraudScannerNotifier {
|
|
2
|
+
currentCommunity: (import("sequelize").Model<YpCommunityData, Partial<YpCommunityData>> & YpCommunityData) | null;
|
|
3
|
+
uniqueCollectionItemsIds: {};
|
|
4
|
+
collectionsToScan: string[];
|
|
5
|
+
scannerModels: (typeof FraudGetPoints)[];
|
|
6
|
+
resetCounts(): void;
|
|
7
|
+
getCommunityURL(): string;
|
|
8
|
+
setupCounts(items: any, collectionType: any): void;
|
|
9
|
+
capitalizeFirstLetter(string: any): any;
|
|
10
|
+
formatNumber(value: any): any;
|
|
11
|
+
getNumberSign(number: any): "" | "+";
|
|
12
|
+
sendNotificationEmails(fraudAuditResults: any): Promise<void>;
|
|
13
|
+
getContainerOldCount(collectionType: any): any;
|
|
14
|
+
getWithDifference(results: any): any;
|
|
15
|
+
notify(): Promise<void>;
|
|
16
|
+
scan(): Promise<void>;
|
|
17
|
+
scanAndNotify(): Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
export function runFraudScannerNotifier(): void;
|
|
20
|
+
import FraudGetPoints = require("./FraudGetPoints.cjs");
|
|
@@ -26,6 +26,8 @@ export declare class YpBaseChatBot {
|
|
|
26
26
|
sendAgentUpdate(message: string): void;
|
|
27
27
|
sendToClient(sender: YpSenderType, message: string, type?: YpAssistantMessageType, uniqueToken?: string | undefined, hiddenContextMessage?: boolean): void;
|
|
28
28
|
streamWebSocketResponses(stream: AsyncIterable<OpenAI.Chat.Completions.ChatCompletionChunk>): Promise<void>;
|
|
29
|
+
usesMaxCompletionTokens(modelName: string): boolean;
|
|
30
|
+
getStreamingChatCompletionParams(messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[]): OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming;
|
|
29
31
|
saveMemoryIfNeeded(): Promise<void>;
|
|
30
32
|
setChatLog(chatLog: YpSimpleChatLog[]): Promise<void>;
|
|
31
33
|
conversation(chatLog: YpSimpleChatLog[]): Promise<void>;
|
|
@@ -41,6 +41,7 @@ export class YpBaseChatBot {
|
|
|
41
41
|
this.wsClientId = wsClientId;
|
|
42
42
|
this.wsClientSocket = wsClients.get(this.wsClientId);
|
|
43
43
|
this.wsClients = wsClients;
|
|
44
|
+
this.memory = { chatLog: [] };
|
|
44
45
|
log.info(`WebSockets: BaseChatBot constructor for ${this.wsClientId}`);
|
|
45
46
|
if (!this.wsClientSocket) {
|
|
46
47
|
log.error(`WebSockets: WS Client ${this.wsClientId} not found in streamWebSocketResponses`);
|
|
@@ -126,8 +127,11 @@ export class YpBaseChatBot {
|
|
|
126
127
|
try {
|
|
127
128
|
let botMessage = "";
|
|
128
129
|
for await (const part of stream) {
|
|
129
|
-
|
|
130
|
-
|
|
130
|
+
const content = part.choices[0].delta.content;
|
|
131
|
+
if (content) {
|
|
132
|
+
this.sendToClient("assistant", content);
|
|
133
|
+
botMessage += content;
|
|
134
|
+
}
|
|
131
135
|
if (part.choices[0].finish_reason == "stop") {
|
|
132
136
|
this.memory.chatLog.push({
|
|
133
137
|
sender: "assistant",
|
|
@@ -149,6 +153,24 @@ export class YpBaseChatBot {
|
|
|
149
153
|
resolve();
|
|
150
154
|
});
|
|
151
155
|
}
|
|
156
|
+
usesMaxCompletionTokens(modelName) {
|
|
157
|
+
return modelName.startsWith("gpt-5");
|
|
158
|
+
}
|
|
159
|
+
getStreamingChatCompletionParams(messages) {
|
|
160
|
+
const requestParams = {
|
|
161
|
+
model: this.llmModel,
|
|
162
|
+
messages,
|
|
163
|
+
stream: true,
|
|
164
|
+
};
|
|
165
|
+
if (this.usesMaxCompletionTokens(this.llmModel)) {
|
|
166
|
+
requestParams.max_completion_tokens = this.maxTokens;
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
requestParams.max_tokens = this.maxTokens;
|
|
170
|
+
requestParams.temperature = this.temperature;
|
|
171
|
+
}
|
|
172
|
+
return requestParams;
|
|
173
|
+
}
|
|
152
174
|
async saveMemoryIfNeeded() {
|
|
153
175
|
if (this.persistMemory) {
|
|
154
176
|
await this.saveMemory();
|
|
@@ -171,13 +193,7 @@ export class YpBaseChatBot {
|
|
|
171
193
|
content: this.renderSystemPrompt(),
|
|
172
194
|
};
|
|
173
195
|
messages.unshift(systemMessage);
|
|
174
|
-
const stream = await this.openaiClient.chat.completions.create(
|
|
175
|
-
model: this.llmModel,
|
|
176
|
-
messages,
|
|
177
|
-
max_tokens: this.maxTokens,
|
|
178
|
-
temperature: this.temperature,
|
|
179
|
-
stream: true,
|
|
180
|
-
});
|
|
196
|
+
const stream = await this.openaiClient.chat.completions.create(this.getStreamingChatCompletionParams(messages));
|
|
181
197
|
this.streamWebSocketResponses(stream);
|
|
182
198
|
}
|
|
183
199
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IImageGenerator, YpAiGenerateImageTypes } from "./iImageGenerator.js";
|
|
1
|
+
import { IImageGenerator, YpAiGenerateImageTypes, YpImageGenerationOptions } from "./iImageGenerator.js";
|
|
2
2
|
export declare class ChatGptImageGenerator implements IImageGenerator {
|
|
3
3
|
private readonly maxRetryCount;
|
|
4
4
|
private readonly openAiKey?;
|
|
@@ -8,6 +8,6 @@ export declare class ChatGptImageGenerator implements IImageGenerator {
|
|
|
8
8
|
* The returned link remains live for ~60 minutes – be sure to download
|
|
9
9
|
* or cache it right away in the calling service.
|
|
10
10
|
*/
|
|
11
|
-
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes): Promise<string | undefined>;
|
|
11
|
+
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes, options?: YpImageGenerationOptions): Promise<string | undefined>;
|
|
12
12
|
}
|
|
13
13
|
export default ChatGptImageGenerator;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
// chatGptImageGenerator.ts
|
|
2
2
|
import { OpenAI } from "openai";
|
|
3
3
|
import log from "../../../utils/loggerTs.js";
|
|
4
|
+
import imageModelConfig from "./imageModelConfig.cjs";
|
|
5
|
+
const { defaultOpenAiImageModel, getDefaultImageQualityForOptions, getDefaultImageSizeForOptions, } = imageModelConfig;
|
|
4
6
|
export class ChatGptImageGenerator {
|
|
5
7
|
constructor(openAiKey) {
|
|
6
8
|
this.maxRetryCount = 3;
|
|
@@ -12,14 +14,15 @@ export class ChatGptImageGenerator {
|
|
|
12
14
|
* The returned link remains live for ~60 minutes – be sure to download
|
|
13
15
|
* or cache it right away in the calling service.
|
|
14
16
|
*/
|
|
15
|
-
async generateImageUrl(prompt, type = "logo") {
|
|
17
|
+
async generateImageUrl(prompt, type = "logo", options) {
|
|
16
18
|
const client = new OpenAI({ apiKey: this.openAiKey });
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
const model = options?.imageModel || defaultOpenAiImageModel;
|
|
20
|
+
const size = options?.imageSize ||
|
|
21
|
+
getDefaultImageSizeForOptions("openai", model, type) ||
|
|
22
|
+
"1536x1024";
|
|
23
|
+
const quality = options?.imageQuality ||
|
|
24
|
+
getDefaultImageQualityForOptions("openai", model) ||
|
|
25
|
+
"medium";
|
|
23
26
|
const finalPrompt = `
|
|
24
27
|
${prompt}
|
|
25
28
|
|
|
@@ -30,11 +33,11 @@ export class ChatGptImageGenerator {
|
|
|
30
33
|
while (retryCount < this.maxRetryCount) {
|
|
31
34
|
try {
|
|
32
35
|
const res = await client.images.generate({
|
|
33
|
-
model
|
|
36
|
+
model,
|
|
34
37
|
prompt: finalPrompt,
|
|
35
|
-
quality:
|
|
38
|
+
quality: quality,
|
|
36
39
|
n: 1,
|
|
37
|
-
size,
|
|
40
|
+
size: size,
|
|
38
41
|
});
|
|
39
42
|
log.info("res", JSON.stringify(res, null, 2));
|
|
40
43
|
const b64Json = res?.data?.[0]?.b64_json;
|
|
@@ -11,12 +11,13 @@ import log from "../../../utils/loggerTs.js";
|
|
|
11
11
|
import models from "../../../models/index.cjs";
|
|
12
12
|
import { ImagenImageGenerator } from "./imagenImageGenerator.js";
|
|
13
13
|
import { ChatGptImageGenerator } from "./chatGptImageGenerator.js";
|
|
14
|
+
import imageModelConfig from "./imageModelConfig.cjs";
|
|
14
15
|
// For reference, in your code:
|
|
15
16
|
const dbModels = models;
|
|
16
17
|
const Image = dbModels.Image;
|
|
17
18
|
const AcBackgroundJob = dbModels.AcBackgroundJob;
|
|
18
19
|
const disableFlux = false;
|
|
19
|
-
const
|
|
20
|
+
const { isOpenAiDalleImageModel, isOpenAiGptImageModel, normalizeImageGenerationOptions, } = imageModelConfig;
|
|
20
21
|
export class CollectionImageGenerator {
|
|
21
22
|
constructor() {
|
|
22
23
|
this.s3Service = new S3Service(process.env.CLOUDFLARE_API_KEY, process.env.CLOUDFLARE_ZONE_ID);
|
|
@@ -29,7 +30,7 @@ export class CollectionImageGenerator {
|
|
|
29
30
|
}
|
|
30
31
|
this.dalleImageGenerator = new DalleImageGenerator(process.env.AZURE_OPENAI_API_BASE, process.env.AZURE_OPENAI_API_KEY, process.env.AZURE_OPENAI_API_DALLE_DEPLOYMENT_NAME, process.env.OPENAI_API_KEY);
|
|
31
32
|
this.chatGptImageGenerator = new ChatGptImageGenerator(process.env.OPENAI_API_KEY);
|
|
32
|
-
if (
|
|
33
|
+
if (process.env.GOOGLE_CLOUD_PROJECT_ID) {
|
|
33
34
|
this.imagenImageGenerator = new ImagenImageGenerator(this.s3Service);
|
|
34
35
|
}
|
|
35
36
|
}
|
|
@@ -44,29 +45,46 @@ export class CollectionImageGenerator {
|
|
|
44
45
|
const s3ImagePath = `ypGenAi/${workPackage.collectionType}/${workPackage.collectionId}/${uuidv4()}.png`;
|
|
45
46
|
try {
|
|
46
47
|
let imageGenerator;
|
|
47
|
-
|
|
48
|
-
if (
|
|
48
|
+
const rawImageOptions = normalizeImageGenerationOptions(workPackage.imageProvider, workPackage.imageModel, workPackage.imageSize, workPackage.imageQuality);
|
|
49
|
+
if (rawImageOptions.error) {
|
|
50
|
+
return reject(rawImageOptions.error);
|
|
51
|
+
}
|
|
52
|
+
const imageOptions = rawImageOptions;
|
|
53
|
+
if (imageOptions.imageProvider === "imagen") {
|
|
54
|
+
if (!this.imagenImageGenerator) {
|
|
55
|
+
return reject("Imagen image generator is not configured.");
|
|
56
|
+
}
|
|
49
57
|
imageGenerator = this.imagenImageGenerator;
|
|
50
|
-
log.info(
|
|
58
|
+
log.info(`Using ImagenImageGenerator: ${imageOptions.imageModel}`);
|
|
51
59
|
}
|
|
52
|
-
else if (
|
|
60
|
+
else if (imageOptions.imageProvider === "flux") {
|
|
61
|
+
if (!this.fluxImageGenerator) {
|
|
62
|
+
return reject("Flux image generator is not configured.");
|
|
63
|
+
}
|
|
53
64
|
imageGenerator = this.fluxImageGenerator;
|
|
54
|
-
log.info(
|
|
65
|
+
log.info(`Using FluxImageGenerator: ${imageOptions.imageModel}`);
|
|
66
|
+
}
|
|
67
|
+
else if (imageOptions.imageProvider === "azureOpenai") {
|
|
68
|
+
imageGenerator = this.dalleImageGenerator;
|
|
69
|
+
log.info(`Using Azure OpenAI image generator: ${imageOptions.imageModel}`);
|
|
70
|
+
}
|
|
71
|
+
else if (isOpenAiDalleImageModel(imageOptions.imageModel)) {
|
|
72
|
+
imageGenerator = this.dalleImageGenerator;
|
|
73
|
+
log.info(`Using DalleImageGenerator: ${imageOptions.imageModel}`);
|
|
55
74
|
}
|
|
56
|
-
else if (
|
|
75
|
+
else if (isOpenAiGptImageModel(imageOptions.imageModel)) {
|
|
57
76
|
imageGenerator = this.chatGptImageGenerator;
|
|
58
|
-
log.info(
|
|
77
|
+
log.info(`Using ChatGptImageGenerator: ${imageOptions.imageModel}`);
|
|
59
78
|
}
|
|
60
79
|
else {
|
|
61
|
-
|
|
62
|
-
log.info("Using DalleImageGenerator");
|
|
80
|
+
return reject(`Unsupported OpenAI image model: ${imageOptions.imageModel}`);
|
|
63
81
|
}
|
|
64
82
|
// 1) Generate image
|
|
65
|
-
const imageUrl = await imageGenerator.generateImageUrl(workPackage.prompt, workPackage.imageType);
|
|
83
|
+
const imageUrl = await imageGenerator.generateImageUrl(workPackage.prompt, workPackage.imageType, imageOptions);
|
|
66
84
|
if (!imageUrl) {
|
|
67
85
|
return reject("Error getting image URL from prompt.");
|
|
68
86
|
}
|
|
69
|
-
if (
|
|
87
|
+
if (imageOptions.imageProvider === "imagen") {
|
|
70
88
|
newImageUrl = imageUrl;
|
|
71
89
|
}
|
|
72
90
|
else {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IImageGenerator, YpAiGenerateImageTypes } from "./iImageGenerator.js";
|
|
1
|
+
import { IImageGenerator, YpAiGenerateImageTypes, YpImageGenerationOptions } from "./iImageGenerator.js";
|
|
2
2
|
export declare class DalleImageGenerator implements IImageGenerator {
|
|
3
3
|
private maxRetryCount;
|
|
4
4
|
private azureOpenaAiBase?;
|
|
@@ -6,5 +6,5 @@ export declare class DalleImageGenerator implements IImageGenerator {
|
|
|
6
6
|
private azureDalleDeployment?;
|
|
7
7
|
private openAiKey?;
|
|
8
8
|
constructor(azureOpenaAiBase: string | undefined, azureOpenAiApiKey: string | undefined, azureDalleDeployment: string | undefined, openAiKey: string | undefined);
|
|
9
|
-
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes): Promise<string | undefined>;
|
|
9
|
+
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes, options?: YpImageGenerationOptions): Promise<string | undefined>;
|
|
10
10
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AzureOpenAI, OpenAI } from "openai";
|
|
2
2
|
import log from "../../../utils/loggerTs.js";
|
|
3
|
+
import imageModelConfig from "./imageModelConfig.cjs";
|
|
4
|
+
const { getDefaultImageQualityForOptions, getDefaultImageSizeForOptions, } = imageModelConfig;
|
|
3
5
|
export class DalleImageGenerator {
|
|
4
6
|
constructor(azureOpenaAiBase, azureOpenAiApiKey, azureDalleDeployment, openAiKey) {
|
|
5
7
|
this.maxRetryCount = 3;
|
|
@@ -8,17 +10,30 @@ export class DalleImageGenerator {
|
|
|
8
10
|
this.azureDalleDeployment = azureDalleDeployment;
|
|
9
11
|
this.openAiKey = openAiKey;
|
|
10
12
|
}
|
|
11
|
-
async generateImageUrl(prompt, type = "logo") {
|
|
13
|
+
async generateImageUrl(prompt, type = "logo", options) {
|
|
12
14
|
let client;
|
|
13
15
|
let result;
|
|
14
16
|
let retryCount = 0;
|
|
15
17
|
let retrying = true;
|
|
18
|
+
const hasAzureOpenAiConfig = Boolean(this.azureOpenaAiBase &&
|
|
19
|
+
this.azureOpenAiApiKey &&
|
|
20
|
+
this.azureDalleDeployment);
|
|
21
|
+
const useAzureOpenAi = options?.imageProvider === "azureOpenai" ||
|
|
22
|
+
(!options?.imageProvider && hasAzureOpenAiConfig);
|
|
23
|
+
const requestedModel = options?.imageModel ||
|
|
24
|
+
(useAzureOpenAi ? this.azureDalleDeployment : undefined) ||
|
|
25
|
+
"dall-e-3";
|
|
26
|
+
const imageProvider = useAzureOpenAi ? "azureOpenai" : "openai";
|
|
16
27
|
// Decide which client to instantiate (Azure vs. standard OpenAI)
|
|
17
|
-
if (
|
|
28
|
+
if (useAzureOpenAi) {
|
|
29
|
+
if (!this.azureOpenaAiBase || !this.azureOpenAiApiKey) {
|
|
30
|
+
log.error("Azure OpenAI image generator is not configured.");
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
18
33
|
client = new AzureOpenAI({
|
|
19
34
|
apiKey: this.azureOpenAiApiKey,
|
|
20
35
|
endpoint: this.azureOpenaAiBase,
|
|
21
|
-
deployment:
|
|
36
|
+
deployment: requestedModel,
|
|
22
37
|
apiVersion: "2024-10-21",
|
|
23
38
|
});
|
|
24
39
|
}
|
|
@@ -28,33 +43,30 @@ export class DalleImageGenerator {
|
|
|
28
43
|
apiKey: this.openAiKey,
|
|
29
44
|
});
|
|
30
45
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
size = "1024x1024";
|
|
38
|
-
}
|
|
39
|
-
const modelQuality = "standard";
|
|
46
|
+
const size = options?.imageSize ||
|
|
47
|
+
getDefaultImageSizeForOptions(imageProvider, requestedModel, type) ||
|
|
48
|
+
"1792x1024";
|
|
49
|
+
const modelQuality = options?.imageQuality ||
|
|
50
|
+
getDefaultImageQualityForOptions(imageProvider, requestedModel) ||
|
|
51
|
+
"standard";
|
|
40
52
|
while (retrying && retryCount < this.maxRetryCount) {
|
|
41
53
|
try {
|
|
42
54
|
// If using Azure OpenAI
|
|
43
|
-
if (
|
|
55
|
+
if (useAzureOpenAi) {
|
|
44
56
|
result = await client.images.generate({
|
|
45
57
|
prompt,
|
|
46
58
|
n: 1,
|
|
47
|
-
size,
|
|
59
|
+
size: size,
|
|
48
60
|
quality: modelQuality,
|
|
49
61
|
});
|
|
50
62
|
}
|
|
51
63
|
else {
|
|
52
64
|
// Standard OpenAI
|
|
53
65
|
result = await client.images.generate({
|
|
54
|
-
model:
|
|
66
|
+
model: requestedModel,
|
|
55
67
|
prompt,
|
|
56
68
|
n: 1,
|
|
57
|
-
size,
|
|
69
|
+
size: size,
|
|
58
70
|
quality: modelQuality,
|
|
59
71
|
});
|
|
60
72
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { IImageGenerator, YpAiGenerateImageTypes } from "./iImageGenerator.js";
|
|
1
|
+
import { IImageGenerator, YpAiGenerateImageTypes, YpImageGenerationOptions } from "./iImageGenerator.js";
|
|
2
2
|
export declare class FluxImageGenerator implements IImageGenerator {
|
|
3
3
|
private replicateApiKey;
|
|
4
4
|
private fluxProModelName;
|
|
5
5
|
private replicate;
|
|
6
6
|
private maxRetryCount;
|
|
7
7
|
constructor(replicateApiKey: string, fluxProModelName: string);
|
|
8
|
-
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes): Promise<string | undefined>;
|
|
8
|
+
generateImageUrl(prompt: string, type?: YpAiGenerateImageTypes, options?: YpImageGenerationOptions): Promise<string | undefined>;
|
|
9
9
|
}
|