nodebb-plugin-openai 1.3.1 → 1.4.0
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/eslint.config.mjs +10 -0
- package/lib/summary.js +8 -9
- package/library.js +14 -4
- package/package.json +8 -6
- package/templates/admin/plugins/openai.tpl +13 -1
- package/.eslintrc +0 -3
- package/public/lib/.eslintrc +0 -3
package/lib/summary.js
CHANGED
|
@@ -8,7 +8,7 @@ const user = require.main.require('./src/user');
|
|
|
8
8
|
|
|
9
9
|
function formatPosts(posts) {
|
|
10
10
|
return posts
|
|
11
|
-
.map(
|
|
11
|
+
.map(post => `User ${post.username}:\n${post.content}`)
|
|
12
12
|
.join('\n---\n');
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -17,7 +17,6 @@ function chunkPosts(posts, maxTokensPerChunk = 3000) {
|
|
|
17
17
|
let currentChunk = [];
|
|
18
18
|
let currentTokens = 0;
|
|
19
19
|
|
|
20
|
-
// eslint-disable-next-line no-restricted-syntax
|
|
21
20
|
for (const post of posts) {
|
|
22
21
|
const estimatedTokens = Math.ceil(post.content.length / 4) + 10;
|
|
23
22
|
if (currentTokens + estimatedTokens > maxTokensPerChunk) {
|
|
@@ -59,7 +58,7 @@ async function summarizeChunk(chunk, openai, settings) {
|
|
|
59
58
|
return response.choices[0].message.content.trim();
|
|
60
59
|
}
|
|
61
60
|
|
|
62
|
-
exports.summarizeTopic = async function(tid, openai, settings) {
|
|
61
|
+
exports.summarizeTopic = async function (tid, openai, settings) {
|
|
63
62
|
const allPids = await topics.getPids(tid);
|
|
64
63
|
|
|
65
64
|
const userMap = {};
|
|
@@ -68,11 +67,11 @@ exports.summarizeTopic = async function(tid, openai, settings) {
|
|
|
68
67
|
|
|
69
68
|
await batch.processArray(allPids, async function (pids) {
|
|
70
69
|
let postData = await posts.getPostsFields(pids, ['uid', 'content', 'deleted']);
|
|
71
|
-
postData = postData.filter(
|
|
70
|
+
postData = postData.filter(p => !p.deleted);
|
|
72
71
|
const missingUids = [];
|
|
73
72
|
postData.forEach((p) => {
|
|
74
73
|
if (!userMap.hasOwnProperty(p.uid)) {
|
|
75
|
-
missingUids.push(p.uid)
|
|
74
|
+
missingUids.push(p.uid);
|
|
76
75
|
}
|
|
77
76
|
});
|
|
78
77
|
const userData = await user.getUsersFields(missingUids, ['username']);
|
|
@@ -86,7 +85,7 @@ exports.summarizeTopic = async function(tid, openai, settings) {
|
|
|
86
85
|
const chunks = chunkPosts(postData);
|
|
87
86
|
|
|
88
87
|
chunkSummaries.push(...await Promise.all(chunks.map(async (chunk) => {
|
|
89
|
-
return summarizeChunk(chunk, openai, settings)
|
|
88
|
+
return summarizeChunk(chunk, openai, settings);
|
|
90
89
|
})));
|
|
91
90
|
}, {
|
|
92
91
|
batch: 500,
|
|
@@ -96,7 +95,7 @@ exports.summarizeTopic = async function(tid, openai, settings) {
|
|
|
96
95
|
return chunkSummaries[0];
|
|
97
96
|
}
|
|
98
97
|
// Final summary from all summaries
|
|
99
|
-
const finalInput = chunkSummaries.join(
|
|
98
|
+
const finalInput = chunkSummaries.join('\n\n');
|
|
100
99
|
if (!finalInput) {
|
|
101
100
|
return '';
|
|
102
101
|
}
|
|
@@ -118,6 +117,6 @@ exports.summarizeTopic = async function(tid, openai, settings) {
|
|
|
118
117
|
return finalResponse.choices[0].message.content.trim();
|
|
119
118
|
};
|
|
120
119
|
|
|
121
|
-
exports.clearTopicSummary = async function(tids) {
|
|
122
|
-
await db.deleteObjectFields(tids.map(
|
|
120
|
+
exports.clearTopicSummary = async function (tids) {
|
|
121
|
+
await db.deleteObjectFields(tids.map(tid => `topic:${tid}`), ['openai:summary']);
|
|
123
122
|
};
|
package/library.js
CHANGED
|
@@ -34,18 +34,27 @@ const defaults = {
|
|
|
34
34
|
|
|
35
35
|
plugin.init = async (params) => {
|
|
36
36
|
const { router /* , middleware , controllers */ } = params;
|
|
37
|
-
const settings = await meta.settings.get('openai')
|
|
37
|
+
const settings = await meta.settings.get('openai');
|
|
38
38
|
if (settings && settings.apikey) {
|
|
39
39
|
openai = new OpenAI({
|
|
40
40
|
apiKey: settings.apikey,
|
|
41
|
+
baseURL: settings.apiBaseUrl || 'https://api.openai.com/v1',
|
|
41
42
|
});
|
|
43
|
+
|
|
44
|
+
// Uncomment the following lines to list available models
|
|
45
|
+
// const list = await openai.models.list();
|
|
46
|
+
// for await (const model of list) {
|
|
47
|
+
// console.log(model.id);
|
|
48
|
+
// }
|
|
49
|
+
|
|
50
|
+
plugin.openai = openai;
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
routeHelpers.setupAdminPageRoute(router, '/admin/plugins/openai', controllers.renderAdminPage);
|
|
45
54
|
};
|
|
46
55
|
|
|
47
56
|
async function getSettings() {
|
|
48
|
-
const settings = await meta.settings.get('openai')
|
|
57
|
+
const settings = await meta.settings.get('openai');
|
|
49
58
|
return {...defaults, ...settings };
|
|
50
59
|
}
|
|
51
60
|
|
|
@@ -150,7 +159,7 @@ plugin.actionMessagingSave = async function (hookData) {
|
|
|
150
159
|
let messages = await messaging.getMessagesFields(mids, ['fromuid', 'content', 'system']);
|
|
151
160
|
messages = messages.filter(m => m && !m.system);
|
|
152
161
|
conversation = messages.map(
|
|
153
|
-
|
|
162
|
+
msg => ({ role: msg.fromuid === chatgptUid ? 'assistant' : 'user', content: msg.content })
|
|
154
163
|
);
|
|
155
164
|
}
|
|
156
165
|
|
|
@@ -180,7 +189,8 @@ async function canUseOpenAI(uid, settings, silent = false) {
|
|
|
180
189
|
|
|
181
190
|
async function checkReputation(uid, settings, silent) {
|
|
182
191
|
const reputation = await user.getUserField(uid, 'reputation');
|
|
183
|
-
const hasEnoughRep = parseInt(settings.minimumReputation, 10) === 0 ||
|
|
192
|
+
const hasEnoughRep = parseInt(settings.minimumReputation, 10) === 0 ||
|
|
193
|
+
parseInt(reputation, 10) >= parseInt(settings.minimumReputation, 10);
|
|
184
194
|
|
|
185
195
|
if (!hasEnoughRep && !silent) {
|
|
186
196
|
sockets.server.in(`uid_${uid}`).emit('event:alert', {
|
package/package.json
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-openai",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "A NodeBB OpenAI plugin",
|
|
5
5
|
"main": "library.js",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/nodebb/nodebb-plugin-openai"
|
|
9
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"lint": "eslint ."
|
|
12
|
+
},
|
|
10
13
|
"keywords": [
|
|
11
14
|
"nodebb",
|
|
12
15
|
"plugin",
|
|
@@ -30,17 +33,16 @@
|
|
|
30
33
|
},
|
|
31
34
|
"readmeFilename": "README.md",
|
|
32
35
|
"nbbpm": {
|
|
33
|
-
"compatibility": "^3.2.0"
|
|
36
|
+
"compatibility": "^3.2.0 || ^4.0.0"
|
|
34
37
|
},
|
|
35
38
|
"dependencies": {
|
|
36
|
-
"openai": "4.
|
|
39
|
+
"openai": "4.103.0"
|
|
37
40
|
},
|
|
38
41
|
"devDependencies": {
|
|
39
42
|
"@commitlint/cli": "19.3.0",
|
|
40
43
|
"@commitlint/config-angular": "19.3.0",
|
|
41
|
-
"eslint": "
|
|
42
|
-
"eslint-config-nodebb": "
|
|
43
|
-
"eslint-plugin-import": "2.29.1",
|
|
44
|
+
"eslint": "9.26.0",
|
|
45
|
+
"eslint-config-nodebb": "^1.1.4",
|
|
44
46
|
"husky": "9.0.11",
|
|
45
47
|
"lint-staged": "15.2.7"
|
|
46
48
|
}
|
|
@@ -11,9 +11,18 @@
|
|
|
11
11
|
<label class="form-label" for="apikey">API Key</label>
|
|
12
12
|
<input type="text" id="apikey" name="apikey" title="API Key" class="form-control">
|
|
13
13
|
<p class="form-text">
|
|
14
|
-
Get your <a href="https://platform.openai.com/api-keys">API Key</a> and enter it above. Requires a restart.
|
|
14
|
+
Get your <a href="https://platform.openai.com/api-keys">API Key</a> and enter it above. You can enter a Google Gemini API key if you want to use Google Gemini. Don't forget to change the model used to a gemini variant. Requires a restart.
|
|
15
15
|
</p>
|
|
16
16
|
</div>
|
|
17
|
+
|
|
18
|
+
<div class="mb-3">
|
|
19
|
+
<label class="form-label" for="apiBaseUrl">API Base Url</label>
|
|
20
|
+
<input type="text" id="apiBaseUrl" name="apiBaseUrl" title="API Base Url" class="form-control">
|
|
21
|
+
<p class="form-text">
|
|
22
|
+
If you want to use Google's Gemini API, enter the base URL here (https://generativelanguage.googleapis.com/v1beta/openai/). Otherwise, leave it blank to use OpenAI's API. Requires a restart.
|
|
23
|
+
</p>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
17
26
|
<div class="mb-3">
|
|
18
27
|
<label class="form-label" for="chatgpt-username">ChatGPT Username</label>
|
|
19
28
|
<input type="text" id="chatgpt-username" name="chatgpt-username" title="ChatGPT Username" class="form-control">
|
|
@@ -38,6 +47,9 @@
|
|
|
38
47
|
<option value="gpt-4o">gpt-4o</option>
|
|
39
48
|
<option value="gpt-4-turbo">gpt-4-turbo</option>
|
|
40
49
|
<option value="gpt-4">gpt-4</option>
|
|
50
|
+
<option value="gpt-4.1-mini">gpt-4.1-mini</option>
|
|
51
|
+
<option value="gpt-4.1">gpt-4.1</option>
|
|
52
|
+
<option value="gemini-2.0-flash">gemini-2.0-flash</option>
|
|
41
53
|
</select>
|
|
42
54
|
</div>
|
|
43
55
|
<div class="">
|
package/.eslintrc
DELETED
package/public/lib/.eslintrc
DELETED