nodebb-plugin-openai 1.4.2 → 1.4.4
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.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "You need at least %1 reputation for mentioning chatgpt",
|
|
3
3
|
"summarize-topic": "Summarize Topic",
|
|
4
4
|
"topic-summary": "Topic Summary",
|
|
5
|
+
"summary-cooldown-error": "You must wait before summarizing a topic again.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "General",
|
|
7
8
|
"admin.apikey": "API Key",
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "You need at least %1 reputation for mentioning chatgpt",
|
|
3
3
|
"summarize-topic": "Summarize Topic",
|
|
4
4
|
"topic-summary": "Topic Summary",
|
|
5
|
+
"summary-cooldown-error": "You must wait before summarizing a topic again.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "General",
|
|
7
8
|
"admin.apikey": "API Key",
|
package/languages/he/openai.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
"error.need-x-reputation-to-mention": "דרושה לך מוניטין של %1 לפחות כדי לאזכר את chatgpt",
|
|
3
3
|
"summarize-topic": "סכם נושא",
|
|
4
4
|
"topic-summary": "סיכום הנושא",
|
|
5
|
+
"summary-cooldown-error": "עליך להמתין לפני שתסכם את הנושא שוב.",
|
|
5
6
|
|
|
6
7
|
"admin.general": "כללי",
|
|
7
8
|
"admin.apikey": "מפתח API",
|
package/library.js
CHANGED
|
@@ -18,6 +18,11 @@ const groups = require.main.require('./src/groups');
|
|
|
18
18
|
const sockets = require.main.require('./src/socket.io');
|
|
19
19
|
const socketPlugins = require.main.require('./src/socket.io/plugins');
|
|
20
20
|
const summary = require('./lib/summary');
|
|
21
|
+
const cacheCreate = require('../cache/lru');
|
|
22
|
+
const summaryCache = cacheCreate({
|
|
23
|
+
name: 'openai-summary',
|
|
24
|
+
max: 200,
|
|
25
|
+
});
|
|
21
26
|
|
|
22
27
|
const plugin = module.exports;
|
|
23
28
|
|
|
@@ -26,7 +31,7 @@ const defaults = {
|
|
|
26
31
|
'chatgpt-username': '',
|
|
27
32
|
enablePrivateMessages: 'off',
|
|
28
33
|
model: 'gpt-3.5-turbo',
|
|
29
|
-
minimumReputation:
|
|
34
|
+
minimumReputation: 2,
|
|
30
35
|
allowedGroups: '[]',
|
|
31
36
|
systemPrompt: 'You are a helpful assistant',
|
|
32
37
|
};
|
|
@@ -125,9 +130,13 @@ plugin.actionMessagingSave = async function (hookData) {
|
|
|
125
130
|
return;
|
|
126
131
|
}
|
|
127
132
|
|
|
133
|
+
// Check actual room membership directly against the database.
|
|
134
|
+
// messaging.isUserInRoom() runs filter:messaging.isUserInRoom, which
|
|
135
|
+
// moderation plugins use to grant virtual access to every room — that
|
|
136
|
+
// would make the bot react to every private chat on the forum.
|
|
128
137
|
const [roomData, inRoom] = await Promise.all([
|
|
129
138
|
messaging.getRoomData(roomId),
|
|
130
|
-
|
|
139
|
+
db.isSortedSetMember(`chat:room:${roomId}:uids`, chatgptUid),
|
|
131
140
|
]);
|
|
132
141
|
|
|
133
142
|
if (!roomData || !inRoom) {
|
|
@@ -145,24 +154,19 @@ plugin.actionMessagingSave = async function (hookData) {
|
|
|
145
154
|
}
|
|
146
155
|
let conversation = [{ role: 'user', content: message.content }];
|
|
147
156
|
if (isPrivate) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
// dont allow chats to get too long
|
|
151
|
-
if (mids.length > 20) {
|
|
152
|
-
await api.chats.post({ uid: chatgptUid, session: {} }, {
|
|
153
|
-
roomId,
|
|
154
|
-
message: 'Conversation too long, please start a new chat',
|
|
155
|
-
toMid: message.mid,
|
|
156
|
-
});
|
|
157
|
-
return;
|
|
158
|
-
}
|
|
157
|
+
// keep context bounded to the 20 most recent messages
|
|
158
|
+
const mids = await getMessageIds(roomId, chatgptUid, 0, 19);
|
|
159
159
|
let messages = await messaging.getMessagesFields(mids, ['fromuid', 'content', 'system']);
|
|
160
|
-
messages = messages.filter(m => m && !m.system);
|
|
160
|
+
messages = messages.filter(m => m && !m.system && m.content);
|
|
161
161
|
conversation = messages.map(
|
|
162
|
-
msg => ({ role: msg.fromuid === chatgptUid ? 'assistant' : 'user', content: msg.content })
|
|
162
|
+
msg => ({ role: parseInt(msg.fromuid, 10) === parseInt(chatgptUid, 10) ? 'assistant' : 'user', content: msg.content })
|
|
163
163
|
);
|
|
164
164
|
}
|
|
165
165
|
|
|
166
|
+
if (!conversation.length) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
166
170
|
const response = await chatComplete(conversation);
|
|
167
171
|
|
|
168
172
|
if (response) {
|
|
@@ -227,17 +231,23 @@ async function checkGroupMembership(uid, settings, silent) {
|
|
|
227
231
|
return memberOfAny;
|
|
228
232
|
}
|
|
229
233
|
|
|
234
|
+
// Returns the newest messages (like core's getMessageIds), in chronological order
|
|
230
235
|
async function getMessageIds(roomId, uid, start, stop) {
|
|
231
236
|
const isPublic = await db.getObjectField(`chat:room:${roomId}`, 'public');
|
|
232
237
|
if (parseInt(isPublic, 10) === 1) {
|
|
233
|
-
|
|
238
|
+
const mids = await db.getSortedSetRevRange(
|
|
234
239
|
`chat:room:${roomId}:mids`, start, stop
|
|
235
240
|
);
|
|
241
|
+
return mids.reverse();
|
|
236
242
|
}
|
|
237
243
|
const userjoinTimestamp = await db.sortedSetScore(`chat:room:${roomId}:uids`, uid);
|
|
238
|
-
|
|
239
|
-
|
|
244
|
+
if (userjoinTimestamp === null) {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
const mids = await db.getSortedSetRevRangeByScore(
|
|
248
|
+
`chat:room:${roomId}:mids`, start, stop === -1 ? -1 : stop - start + 1, '+inf', userjoinTimestamp
|
|
240
249
|
);
|
|
250
|
+
return mids.reverse();
|
|
241
251
|
}
|
|
242
252
|
|
|
243
253
|
async function chatComplete(messages) {
|
|
@@ -325,16 +335,22 @@ socketPlugins.openai.summarizeTopic = async function (socket, data) {
|
|
|
325
335
|
if (!await privileges.topics.can('topics:read', tid, socket.uid)) {
|
|
326
336
|
throw new Error('[[error:no-privileges]]');
|
|
327
337
|
}
|
|
328
|
-
const settings = await getSettings();
|
|
329
|
-
if (!await canUseOpenAI(socket.uid, settings)) {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
338
|
|
|
333
339
|
let openaiSummary = await topics.getTopicField(tid, 'openai:summary');
|
|
334
340
|
if (openaiSummary) {
|
|
335
341
|
return openaiSummary;
|
|
336
342
|
}
|
|
343
|
+
const settings = await getSettings();
|
|
344
|
+
if (!await canUseOpenAI(socket.uid, settings)) {
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
const cooldown = summaryCache.get(`uid:${socket.uid}`);
|
|
348
|
+
if (cooldown) {
|
|
349
|
+
throw new Error('[[openai:summary-cooldown-error]]');
|
|
350
|
+
}
|
|
351
|
+
|
|
337
352
|
openaiSummary = await summary.summarizeTopic(tid, openai, settings);
|
|
353
|
+
summaryCache.set(`uid:${socket.uid}`, 1, 60000);
|
|
338
354
|
await topics.setTopicField(tid, 'openai:summary', openaiSummary);
|
|
339
355
|
return openaiSummary;
|
|
340
356
|
};
|