nodebb-plugin-integration 0.3.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.
@@ -0,0 +1,383 @@
1
+ "use strict";
2
+
3
+ /*
4
+
5
+ NodeBB Integration Layer
6
+
7
+
8
+ Knowledge Indexer
9
+
10
+
11
+
12
+ */
13
+
14
+
15
+
16
+
17
+ const Embeddings =
18
+ require("./embeddings");
19
+
20
+ const Qdrant =
21
+ require("./qdrant");
22
+
23
+ const crypto =
24
+ require("crypto");
25
+
26
+
27
+
28
+
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+
38
+ function createId(prefix, id) {
39
+
40
+ /*
41
+ * Qdrant supports UUID or integer.
42
+ * Create deterministic UUID from source id.
43
+ */
44
+
45
+
46
+ return crypto
47
+
48
+ .createHash("sha256")
49
+
50
+ .update(
51
+
52
+ `${prefix}:${id}`
53
+
54
+ )
55
+
56
+ .digest("hex")
57
+
58
+ .substring(0, 32);
59
+
60
+ }
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+ async function indexDocument(
75
+
76
+ type,
77
+
78
+ id,
79
+
80
+ text,
81
+
82
+ payload = {}
83
+
84
+ ) {
85
+
86
+
87
+
88
+
89
+ if(!text || !text.trim()) {
90
+
91
+
92
+ return false;
93
+
94
+
95
+ }
96
+
97
+
98
+
99
+
100
+
101
+
102
+
103
+ const vector =
104
+
105
+ await Embeddings.create(
106
+
107
+ text
108
+
109
+ );
110
+
111
+
112
+
113
+
114
+
115
+
116
+
117
+ if(!vector) {
118
+
119
+
120
+ console.log(
121
+
122
+ "[Indexer] embedding unavailable"
123
+
124
+ );
125
+
126
+
127
+ return false;
128
+
129
+
130
+ }
131
+
132
+
133
+
134
+
135
+
136
+
137
+
138
+ await Qdrant.ensureCollection();
139
+
140
+
141
+
142
+
143
+
144
+
145
+
146
+
147
+ const pointId =
148
+
149
+ createId(
150
+
151
+ type,
152
+
153
+ id
154
+
155
+ );
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+ const result =
164
+
165
+ await Qdrant.upsert(
166
+
167
+ pointId,
168
+
169
+ vector,
170
+
171
+ {
172
+
173
+
174
+ type,
175
+
176
+
177
+ sourceId: id,
178
+
179
+
180
+ text,
181
+
182
+
183
+ ...payload
184
+
185
+
186
+ }
187
+
188
+
189
+ );
190
+
191
+
192
+
193
+
194
+
195
+
196
+
197
+
198
+ if(result) {
199
+
200
+
201
+
202
+ console.log(
203
+
204
+ `[Indexer] indexed ${type}:${id}`
205
+
206
+ );
207
+
208
+
209
+ }
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+ return result;
218
+
219
+
220
+
221
+
222
+ }
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+
231
+
232
+
233
+
234
+
235
+
236
+ /*
237
+
238
+ Index post
239
+ */
240
+
241
+ async function indexPost(post) {
242
+
243
+
244
+
245
+
246
+ if(!post) {
247
+
248
+
249
+ return false;
250
+
251
+
252
+ }
253
+
254
+
255
+
256
+
257
+
258
+
259
+
260
+ return indexDocument(
261
+
262
+ "post",
263
+
264
+ post.pid,
265
+
266
+
267
+ post.content || post.text || "",
268
+
269
+
270
+ {
271
+
272
+
273
+ pid:
274
+ post.pid,
275
+
276
+
277
+ uid:
278
+ post.uid,
279
+
280
+
281
+ tid:
282
+ post.tid
283
+
284
+
285
+
286
+ }
287
+
288
+
289
+ );
290
+
291
+ }
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+ /*
306
+
307
+ Index topic
308
+ */
309
+
310
+ async function indexTopic(topic) {
311
+
312
+
313
+
314
+
315
+ if(!topic) {
316
+
317
+
318
+ return false;
319
+
320
+
321
+ }
322
+
323
+
324
+
325
+
326
+
327
+
328
+
329
+ return indexDocument(
330
+
331
+ "topic",
332
+
333
+ topic.tid,
334
+
335
+
336
+ topic.title || "",
337
+
338
+
339
+ {
340
+
341
+
342
+ tid:
343
+ topic.tid,
344
+
345
+
346
+ uid:
347
+ topic.uid
348
+
349
+
350
+
351
+ }
352
+
353
+
354
+ );
355
+
356
+ }
357
+
358
+
359
+
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+ module.exports = {
371
+
372
+ indexPost,
373
+
374
+
375
+ indexTopic,
376
+
377
+
378
+ indexDocument
379
+
380
+
381
+
382
+
383
+ };
@@ -0,0 +1,331 @@
1
+ "use strict";
2
+
3
+
4
+ /*
5
+ * NodeBB Integration Layer
6
+ *
7
+ * OpenAI Adapter
8
+ *
9
+ * Provides:
10
+ *
11
+ * - Chat completion
12
+ * - Embeddings
13
+ *
14
+ * Safe modes:
15
+ *
16
+ * - Development
17
+ * - Test
18
+ * - Production
19
+ */
20
+
21
+
22
+
23
+ const Config =
24
+ require("../config");
25
+
26
+
27
+
28
+ let client = null;
29
+
30
+
31
+
32
+
33
+
34
+
35
+
36
+
37
+ /*
38
+ * Check API availability
39
+ */
40
+
41
+
42
+ function isAvailable() {
43
+
44
+
45
+ return Boolean(
46
+
47
+ Config.openai.apiKey
48
+
49
+ );
50
+
51
+
52
+ }
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+ /*
63
+ * Lazy OpenAI client creation
64
+ */
65
+
66
+
67
+ async function getClient() {
68
+
69
+
70
+
71
+ if(client) {
72
+
73
+
74
+ return client;
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
+
331
+ };