nodebb-plugin-integration 0.4.1 → 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/src/ai/openai.js CHANGED
@@ -1,331 +1,75 @@
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
-
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
  };