nodebb-plugin-integration 0.4.2 → 0.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.
- package/README.md +199 -320
- package/README.ru.md +199 -347
- package/library.js +93 -115
- package/package.json +60 -57
- package/plugin.json +50 -33
- package/public/js/admin.js +58 -45
- package/public/templates/admin/plugins/integration.tpl +213 -0
- package/src/ai/embeddings.js +46 -253
- package/src/ai/indexer.js +156 -343
- package/src/ai/openai.js +74 -330
- package/src/ai/qdrant.js +114 -517
- package/src/ai/search.js +138 -251
- package/src/config/index.js +176 -133
- package/src/config/validation.js +250 -0
- package/src/events/index.js +174 -324
- package/src/mcp/manager.js +396 -0
- package/src/mcp/server.js +338 -172
- package/src/mcp/tools.js +158 -617
- package/src/nodebb/categories.js +39 -0
- package/src/nodebb/index.js +72 -305
- package/src/nodebb/meta.js +24 -273
- package/src/nodebb/posts.js +93 -348
- package/src/nodebb/privileges.js +68 -0
- package/src/nodebb/topics.js +91 -355
- package/src/nodebb/users.js +62 -345
- package/src/routes/api.js +255 -97
- package/src/routes/index.js +64 -49
- package/templates/admin/plugins/integration.tpl +0 -130
package/src/ai/openai.js
CHANGED
|
@@ -1,331 +1,75 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
if(!isAvailable()) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
console.log(
|
|
89
|
-
|
|
90
|
-
"[OpenAI] API key not configured"
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return null;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const OpenAI =
|
|
112
|
-
require("openai");
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
client =
|
|
119
|
-
new OpenAI({
|
|
120
|
-
|
|
121
|
-
apiKey:
|
|
122
|
-
Config.openai.apiKey
|
|
123
|
-
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
return client;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
catch(error) {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
console.error(
|
|
142
|
-
|
|
143
|
-
"[OpenAI] initialization failed",
|
|
144
|
-
|
|
145
|
-
error
|
|
146
|
-
|
|
147
|
-
);
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return null;
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
/*
|
|
168
|
-
* Create embedding vector
|
|
169
|
-
*/
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
async function embeddings(text) {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const openai =
|
|
177
|
-
await getClient();
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if(!openai) {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return null;
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if(!text || !text.trim()) {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
return null;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
const response =
|
|
216
|
-
await openai.embeddings.create({
|
|
217
|
-
|
|
218
|
-
model:
|
|
219
|
-
Config.openai.embeddingModel,
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
input:
|
|
223
|
-
text
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
});
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return response
|
|
234
|
-
.data[0]
|
|
235
|
-
.embedding;
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
/*
|
|
249
|
-
* Chat completion
|
|
250
|
-
*/
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
async function chat(messages) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const openai =
|
|
258
|
-
await getClient();
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
if(!openai) {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
return null;
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const response =
|
|
279
|
-
await openai.chat.completions.create({
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
model:
|
|
283
|
-
Config.openai.model,
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
messages
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
return response
|
|
300
|
-
.choices[0]
|
|
301
|
-
.message
|
|
302
|
-
.content;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
module.exports = {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
isAvailable,
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
getClient,
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
embeddings,
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
chat
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const OpenAI = require("openai");
|
|
4
|
+
const Config = require("../config");
|
|
5
|
+
|
|
6
|
+
let client = null;
|
|
7
|
+
let clientKey = null;
|
|
8
|
+
|
|
9
|
+
async function getClient() {
|
|
10
|
+
const settings = await Config.get();
|
|
11
|
+
|
|
12
|
+
if (settings.aiProvider !== "openai") {
|
|
13
|
+
const error = new Error(
|
|
14
|
+
"OpenAI is disabled because AI provider is set to fake"
|
|
15
|
+
);
|
|
16
|
+
error.code = "AI_DISABLED";
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!settings.openaiApiKey) {
|
|
21
|
+
throw new Error("OpenAI API key is not configured");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (client && clientKey === settings.openaiApiKey) {
|
|
25
|
+
return client;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
client = new OpenAI({
|
|
29
|
+
apiKey: settings.openaiApiKey,
|
|
30
|
+
timeout: 60000,
|
|
31
|
+
maxRetries: 2
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
clientKey = settings.openaiApiKey;
|
|
35
|
+
|
|
36
|
+
return client;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function createEmbedding(input) {
|
|
40
|
+
if (typeof input !== "string" || !input.trim()) {
|
|
41
|
+
throw new TypeError("Embedding input must be a non-empty string");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const settings = await Config.get();
|
|
45
|
+
const openai = await getClient();
|
|
46
|
+
|
|
47
|
+
const response = await openai.embeddings.create({
|
|
48
|
+
model: settings.openaiModel,
|
|
49
|
+
input: input.trim(),
|
|
50
|
+
dimensions: settings.embeddingSize,
|
|
51
|
+
encoding_format: "float"
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const embedding = response.data?.[0]?.embedding;
|
|
55
|
+
|
|
56
|
+
if (!Array.isArray(embedding)) {
|
|
57
|
+
throw new Error("OpenAI returned an invalid embedding");
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (embedding.length !== settings.embeddingSize) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Embedding dimension mismatch: expected ${settings.embeddingSize}, received ${embedding.length}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!embedding.every(Number.isFinite)) {
|
|
67
|
+
throw new Error("OpenAI returned non-finite embedding values");
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return embedding;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
createEmbedding
|
|
331
75
|
};
|