strapi-plugin-faqchatbot 1.0.0 → 1.0.2
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/dist/_chunks/{App-Cn0wk9Bd.js → App-BegikOzF.js} +28 -12
- package/dist/_chunks/{App-B02Agl-6.mjs → App-_ybXHESK.mjs} +28 -12
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +45 -19
- package/dist/server/index.mjs +45 -19
- package/package.json +1 -1
|
@@ -25026,20 +25026,36 @@ const ChatbotPreview = () => {
|
|
|
25026
25026
|
const res = await fetch("/api/faqchatbot/ask", {
|
|
25027
25027
|
method: "POST",
|
|
25028
25028
|
headers: { "Content-Type": "application/json" },
|
|
25029
|
-
body: JSON.stringify({
|
|
25030
|
-
question: userText
|
|
25031
|
-
})
|
|
25029
|
+
body: JSON.stringify({ question: userText })
|
|
25032
25030
|
});
|
|
25033
|
-
|
|
25034
|
-
|
|
25035
|
-
|
|
25036
|
-
|
|
25037
|
-
|
|
25031
|
+
if (!res.ok) throw new Error("Request failed");
|
|
25032
|
+
const reader = res.body?.getReader();
|
|
25033
|
+
if (!reader) throw new Error("No stream");
|
|
25034
|
+
let botMessage = "";
|
|
25035
|
+
let doneStream = false;
|
|
25036
|
+
setMessages((prev) => [...prev, { text: "", isUser: false }]);
|
|
25037
|
+
while (!doneStream) {
|
|
25038
|
+
const { done, value } = await reader.read();
|
|
25039
|
+
if (done) break;
|
|
25040
|
+
const chunk = new TextDecoder().decode(value);
|
|
25041
|
+
const lines = chunk.split("\n");
|
|
25042
|
+
for (const line of lines) {
|
|
25043
|
+
if (!line.startsWith("data: ")) continue;
|
|
25044
|
+
const text = line.replace("data: ", "").trim();
|
|
25045
|
+
if (text === "[DONE]") {
|
|
25046
|
+
doneStream = true;
|
|
25047
|
+
break;
|
|
25048
|
+
}
|
|
25049
|
+
botMessage += text;
|
|
25050
|
+
setMessages((prev) => {
|
|
25051
|
+
const updated = [...prev];
|
|
25052
|
+
updated[updated.length - 1] = { text: botMessage, isUser: false };
|
|
25053
|
+
return updated;
|
|
25054
|
+
});
|
|
25055
|
+
}
|
|
25056
|
+
}
|
|
25038
25057
|
} catch (err) {
|
|
25039
|
-
setMessages((prev) => [
|
|
25040
|
-
...prev,
|
|
25041
|
-
{ text: "Error contacting chatbot", isUser: false }
|
|
25042
|
-
]);
|
|
25058
|
+
setMessages((prev) => [...prev, { text: "Error contacting chatbot", isUser: false }]);
|
|
25043
25059
|
}
|
|
25044
25060
|
};
|
|
25045
25061
|
const handleClearHistory = () => {
|
|
@@ -25025,20 +25025,36 @@ const ChatbotPreview = () => {
|
|
|
25025
25025
|
const res = await fetch("/api/faqchatbot/ask", {
|
|
25026
25026
|
method: "POST",
|
|
25027
25027
|
headers: { "Content-Type": "application/json" },
|
|
25028
|
-
body: JSON.stringify({
|
|
25029
|
-
question: userText
|
|
25030
|
-
})
|
|
25028
|
+
body: JSON.stringify({ question: userText })
|
|
25031
25029
|
});
|
|
25032
|
-
|
|
25033
|
-
|
|
25034
|
-
|
|
25035
|
-
|
|
25036
|
-
|
|
25030
|
+
if (!res.ok) throw new Error("Request failed");
|
|
25031
|
+
const reader = res.body?.getReader();
|
|
25032
|
+
if (!reader) throw new Error("No stream");
|
|
25033
|
+
let botMessage = "";
|
|
25034
|
+
let doneStream = false;
|
|
25035
|
+
setMessages((prev) => [...prev, { text: "", isUser: false }]);
|
|
25036
|
+
while (!doneStream) {
|
|
25037
|
+
const { done, value } = await reader.read();
|
|
25038
|
+
if (done) break;
|
|
25039
|
+
const chunk = new TextDecoder().decode(value);
|
|
25040
|
+
const lines = chunk.split("\n");
|
|
25041
|
+
for (const line of lines) {
|
|
25042
|
+
if (!line.startsWith("data: ")) continue;
|
|
25043
|
+
const text = line.replace("data: ", "").trim();
|
|
25044
|
+
if (text === "[DONE]") {
|
|
25045
|
+
doneStream = true;
|
|
25046
|
+
break;
|
|
25047
|
+
}
|
|
25048
|
+
botMessage += text;
|
|
25049
|
+
setMessages((prev) => {
|
|
25050
|
+
const updated = [...prev];
|
|
25051
|
+
updated[updated.length - 1] = { text: botMessage, isUser: false };
|
|
25052
|
+
return updated;
|
|
25053
|
+
});
|
|
25054
|
+
}
|
|
25055
|
+
}
|
|
25037
25056
|
} catch (err) {
|
|
25038
|
-
setMessages((prev) => [
|
|
25039
|
-
...prev,
|
|
25040
|
-
{ text: "Error contacting chatbot", isUser: false }
|
|
25041
|
-
]);
|
|
25057
|
+
setMessages((prev) => [...prev, { text: "Error contacting chatbot", isUser: false }]);
|
|
25042
25058
|
}
|
|
25043
25059
|
};
|
|
25044
25060
|
const handleClearHistory = () => {
|
package/dist/admin/index.js
CHANGED
|
@@ -37,7 +37,7 @@ const index = {
|
|
|
37
37
|
defaultMessage: PLUGIN_ID
|
|
38
38
|
},
|
|
39
39
|
Component: async () => {
|
|
40
|
-
const { App } = await Promise.resolve().then(() => require("../_chunks/App-
|
|
40
|
+
const { App } = await Promise.resolve().then(() => require("../_chunks/App-BegikOzF.js"));
|
|
41
41
|
return App;
|
|
42
42
|
}
|
|
43
43
|
});
|
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -97,9 +97,19 @@ const config$1 = ({ strapi }) => ({
|
|
|
97
97
|
ctx.body = data;
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
100
|
+
async function getOpenAI$1(strapi) {
|
|
101
|
+
const pluginStore = strapi.store({
|
|
102
|
+
environment: null,
|
|
103
|
+
type: "plugin",
|
|
104
|
+
name: "faqchatbot"
|
|
105
|
+
});
|
|
106
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
107
|
+
const key = settings?.openaiKey;
|
|
108
|
+
if (!key) {
|
|
109
|
+
throw new Error("OpenAI key not configured in plugin settings");
|
|
110
|
+
}
|
|
111
|
+
return new OpenAI__default.default({ apiKey: key });
|
|
112
|
+
}
|
|
103
113
|
async function getContactLink(strapi) {
|
|
104
114
|
const pluginStore = strapi.store({
|
|
105
115
|
environment: null,
|
|
@@ -171,12 +181,13 @@ async function getActiveCollections(strapi) {
|
|
|
171
181
|
return [];
|
|
172
182
|
}
|
|
173
183
|
}
|
|
174
|
-
async function rephraseQuestion(history, question) {
|
|
184
|
+
async function rephraseQuestion(strapi, history, question) {
|
|
175
185
|
if (!history || !Array.isArray(history) || history.length === 0) {
|
|
176
186
|
return question;
|
|
177
187
|
}
|
|
178
188
|
try {
|
|
179
|
-
const
|
|
189
|
+
const openai = await getOpenAI$1(strapi);
|
|
190
|
+
const response = await openai.chat.completions.create({
|
|
180
191
|
model: "gpt-4o-mini",
|
|
181
192
|
temperature: 0,
|
|
182
193
|
messages: [
|
|
@@ -318,7 +329,8 @@ function cosineSimilarity(a, b) {
|
|
|
318
329
|
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
319
330
|
}
|
|
320
331
|
async function searchFAQ(question, strapi) {
|
|
321
|
-
const
|
|
332
|
+
const openai = await getOpenAI$1(strapi);
|
|
333
|
+
const embedding = await openai.embeddings.create({
|
|
322
334
|
model: "text-embedding-3-small",
|
|
323
335
|
input: question
|
|
324
336
|
});
|
|
@@ -352,8 +364,9 @@ async function searchFAQ(question, strapi) {
|
|
|
352
364
|
}
|
|
353
365
|
return scored.slice(0, 3).map((s) => s.answer);
|
|
354
366
|
}
|
|
355
|
-
async function simplePlanner(question, activeCollections, instructions) {
|
|
356
|
-
const
|
|
367
|
+
async function simplePlanner(strapi, question, activeCollections, instructions) {
|
|
368
|
+
const openai = await getOpenAI$1(strapi);
|
|
369
|
+
const response = await openai.chat.completions.create({
|
|
357
370
|
model: "gpt-4o-mini",
|
|
358
371
|
temperature: 0,
|
|
359
372
|
messages: [
|
|
@@ -524,9 +537,10 @@ ${JSON.stringify(activeCollections, null, 2)}
|
|
|
524
537
|
return null;
|
|
525
538
|
}
|
|
526
539
|
}
|
|
527
|
-
async function realtimeInterpreterAI(question, realtimeData) {
|
|
540
|
+
async function realtimeInterpreterAI(strapi, question, realtimeData) {
|
|
528
541
|
if (!realtimeData) return null;
|
|
529
|
-
const
|
|
542
|
+
const openai = await getOpenAI$1(strapi);
|
|
543
|
+
const response = await openai.chat.completions.create({
|
|
530
544
|
model: "gpt-4o-mini",
|
|
531
545
|
temperature: 0.2,
|
|
532
546
|
messages: [
|
|
@@ -559,13 +573,14 @@ ${JSON.stringify(realtimeData)}
|
|
|
559
573
|
const text = response.choices[0].message.content;
|
|
560
574
|
return text;
|
|
561
575
|
}
|
|
562
|
-
async function finalAggregator(ctx, question, faq, realtimeMeta, realtimeText, contactLink, instructions) {
|
|
576
|
+
async function finalAggregator(strapi, ctx, question, faq, realtimeMeta, realtimeText, contactLink, instructions) {
|
|
563
577
|
ctx.set("Content-Type", "text/event-stream");
|
|
564
578
|
ctx.set("Cache-Control", "no-cache");
|
|
565
579
|
ctx.set("Connection", "keep-alive");
|
|
566
580
|
ctx.status = 200;
|
|
567
581
|
ctx.res.flushHeaders?.();
|
|
568
|
-
const
|
|
582
|
+
const openai = await getOpenAI$1(strapi);
|
|
583
|
+
const stream = await openai.chat.completions.create({
|
|
569
584
|
model: "gpt-4o-mini",
|
|
570
585
|
temperature: 0.3,
|
|
571
586
|
stream: true,
|
|
@@ -704,18 +719,19 @@ const ask = ({ strapi }) => ({
|
|
|
704
719
|
const activeCollections = await getActiveCollections(strapi);
|
|
705
720
|
if (!activeCollections || activeCollections.length === 0) {
|
|
706
721
|
}
|
|
707
|
-
const rewritten = await rephraseQuestion(history, question);
|
|
722
|
+
const rewritten = await rephraseQuestion(strapi, history, question);
|
|
708
723
|
const contactLink = await getContactLink(strapi);
|
|
709
724
|
const faqResults = await searchFAQ(rewritten, strapi);
|
|
710
|
-
const plan = await simplePlanner(rewritten, activeCollections, instructions);
|
|
725
|
+
const plan = await simplePlanner(strapi, rewritten, activeCollections, instructions);
|
|
711
726
|
let realtimeResults = null;
|
|
712
727
|
let realtimeAIText = null;
|
|
713
728
|
if (plan && plan.collection) {
|
|
714
729
|
realtimeResults = await searchRealtime(strapi, plan, activeCollections);
|
|
715
|
-
realtimeAIText = await realtimeInterpreterAI(rewritten, realtimeResults);
|
|
730
|
+
realtimeAIText = await realtimeInterpreterAI(strapi, rewritten, realtimeResults);
|
|
716
731
|
} else {
|
|
717
732
|
}
|
|
718
733
|
await finalAggregator(
|
|
734
|
+
strapi,
|
|
719
735
|
ctx,
|
|
720
736
|
rewritten,
|
|
721
737
|
faqResults,
|
|
@@ -834,13 +850,23 @@ const routes = {
|
|
|
834
850
|
admin,
|
|
835
851
|
"content-api": contentApi
|
|
836
852
|
};
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
853
|
+
async function getOpenAI(strapi) {
|
|
854
|
+
const pluginStore = strapi.store({
|
|
855
|
+
environment: null,
|
|
856
|
+
type: "plugin",
|
|
857
|
+
name: "faqchatbot"
|
|
858
|
+
});
|
|
859
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
860
|
+
const key = settings?.openaiKey;
|
|
861
|
+
if (!key) return null;
|
|
862
|
+
return new OpenAI__default.default({ apiKey: key });
|
|
863
|
+
}
|
|
840
864
|
const embed = ({ strapi }) => ({
|
|
841
865
|
async generateEmbedding(text) {
|
|
842
866
|
try {
|
|
843
|
-
|
|
867
|
+
const openai = await getOpenAI(strapi);
|
|
868
|
+
if (!openai) {
|
|
869
|
+
strapi.log.warn("OpenAI key missing – skipping embedding");
|
|
844
870
|
return null;
|
|
845
871
|
}
|
|
846
872
|
const response = await openai.embeddings.create({
|
package/dist/server/index.mjs
CHANGED
|
@@ -94,9 +94,19 @@ const config$1 = ({ strapi }) => ({
|
|
|
94
94
|
ctx.body = data;
|
|
95
95
|
}
|
|
96
96
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
async function getOpenAI$1(strapi) {
|
|
98
|
+
const pluginStore = strapi.store({
|
|
99
|
+
environment: null,
|
|
100
|
+
type: "plugin",
|
|
101
|
+
name: "faqchatbot"
|
|
102
|
+
});
|
|
103
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
104
|
+
const key = settings?.openaiKey;
|
|
105
|
+
if (!key) {
|
|
106
|
+
throw new Error("OpenAI key not configured in plugin settings");
|
|
107
|
+
}
|
|
108
|
+
return new OpenAI({ apiKey: key });
|
|
109
|
+
}
|
|
100
110
|
async function getContactLink(strapi) {
|
|
101
111
|
const pluginStore = strapi.store({
|
|
102
112
|
environment: null,
|
|
@@ -168,12 +178,13 @@ async function getActiveCollections(strapi) {
|
|
|
168
178
|
return [];
|
|
169
179
|
}
|
|
170
180
|
}
|
|
171
|
-
async function rephraseQuestion(history, question) {
|
|
181
|
+
async function rephraseQuestion(strapi, history, question) {
|
|
172
182
|
if (!history || !Array.isArray(history) || history.length === 0) {
|
|
173
183
|
return question;
|
|
174
184
|
}
|
|
175
185
|
try {
|
|
176
|
-
const
|
|
186
|
+
const openai = await getOpenAI$1(strapi);
|
|
187
|
+
const response = await openai.chat.completions.create({
|
|
177
188
|
model: "gpt-4o-mini",
|
|
178
189
|
temperature: 0,
|
|
179
190
|
messages: [
|
|
@@ -315,7 +326,8 @@ function cosineSimilarity(a, b) {
|
|
|
315
326
|
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
316
327
|
}
|
|
317
328
|
async function searchFAQ(question, strapi) {
|
|
318
|
-
const
|
|
329
|
+
const openai = await getOpenAI$1(strapi);
|
|
330
|
+
const embedding = await openai.embeddings.create({
|
|
319
331
|
model: "text-embedding-3-small",
|
|
320
332
|
input: question
|
|
321
333
|
});
|
|
@@ -349,8 +361,9 @@ async function searchFAQ(question, strapi) {
|
|
|
349
361
|
}
|
|
350
362
|
return scored.slice(0, 3).map((s) => s.answer);
|
|
351
363
|
}
|
|
352
|
-
async function simplePlanner(question, activeCollections, instructions) {
|
|
353
|
-
const
|
|
364
|
+
async function simplePlanner(strapi, question, activeCollections, instructions) {
|
|
365
|
+
const openai = await getOpenAI$1(strapi);
|
|
366
|
+
const response = await openai.chat.completions.create({
|
|
354
367
|
model: "gpt-4o-mini",
|
|
355
368
|
temperature: 0,
|
|
356
369
|
messages: [
|
|
@@ -521,9 +534,10 @@ ${JSON.stringify(activeCollections, null, 2)}
|
|
|
521
534
|
return null;
|
|
522
535
|
}
|
|
523
536
|
}
|
|
524
|
-
async function realtimeInterpreterAI(question, realtimeData) {
|
|
537
|
+
async function realtimeInterpreterAI(strapi, question, realtimeData) {
|
|
525
538
|
if (!realtimeData) return null;
|
|
526
|
-
const
|
|
539
|
+
const openai = await getOpenAI$1(strapi);
|
|
540
|
+
const response = await openai.chat.completions.create({
|
|
527
541
|
model: "gpt-4o-mini",
|
|
528
542
|
temperature: 0.2,
|
|
529
543
|
messages: [
|
|
@@ -556,13 +570,14 @@ ${JSON.stringify(realtimeData)}
|
|
|
556
570
|
const text = response.choices[0].message.content;
|
|
557
571
|
return text;
|
|
558
572
|
}
|
|
559
|
-
async function finalAggregator(ctx, question, faq, realtimeMeta, realtimeText, contactLink, instructions) {
|
|
573
|
+
async function finalAggregator(strapi, ctx, question, faq, realtimeMeta, realtimeText, contactLink, instructions) {
|
|
560
574
|
ctx.set("Content-Type", "text/event-stream");
|
|
561
575
|
ctx.set("Cache-Control", "no-cache");
|
|
562
576
|
ctx.set("Connection", "keep-alive");
|
|
563
577
|
ctx.status = 200;
|
|
564
578
|
ctx.res.flushHeaders?.();
|
|
565
|
-
const
|
|
579
|
+
const openai = await getOpenAI$1(strapi);
|
|
580
|
+
const stream = await openai.chat.completions.create({
|
|
566
581
|
model: "gpt-4o-mini",
|
|
567
582
|
temperature: 0.3,
|
|
568
583
|
stream: true,
|
|
@@ -701,18 +716,19 @@ const ask = ({ strapi }) => ({
|
|
|
701
716
|
const activeCollections = await getActiveCollections(strapi);
|
|
702
717
|
if (!activeCollections || activeCollections.length === 0) {
|
|
703
718
|
}
|
|
704
|
-
const rewritten = await rephraseQuestion(history, question);
|
|
719
|
+
const rewritten = await rephraseQuestion(strapi, history, question);
|
|
705
720
|
const contactLink = await getContactLink(strapi);
|
|
706
721
|
const faqResults = await searchFAQ(rewritten, strapi);
|
|
707
|
-
const plan = await simplePlanner(rewritten, activeCollections, instructions);
|
|
722
|
+
const plan = await simplePlanner(strapi, rewritten, activeCollections, instructions);
|
|
708
723
|
let realtimeResults = null;
|
|
709
724
|
let realtimeAIText = null;
|
|
710
725
|
if (plan && plan.collection) {
|
|
711
726
|
realtimeResults = await searchRealtime(strapi, plan, activeCollections);
|
|
712
|
-
realtimeAIText = await realtimeInterpreterAI(rewritten, realtimeResults);
|
|
727
|
+
realtimeAIText = await realtimeInterpreterAI(strapi, rewritten, realtimeResults);
|
|
713
728
|
} else {
|
|
714
729
|
}
|
|
715
730
|
await finalAggregator(
|
|
731
|
+
strapi,
|
|
716
732
|
ctx,
|
|
717
733
|
rewritten,
|
|
718
734
|
faqResults,
|
|
@@ -831,13 +847,23 @@ const routes = {
|
|
|
831
847
|
admin,
|
|
832
848
|
"content-api": contentApi
|
|
833
849
|
};
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
850
|
+
async function getOpenAI(strapi) {
|
|
851
|
+
const pluginStore = strapi.store({
|
|
852
|
+
environment: null,
|
|
853
|
+
type: "plugin",
|
|
854
|
+
name: "faqchatbot"
|
|
855
|
+
});
|
|
856
|
+
const settings = await pluginStore.get({ key: "settings" });
|
|
857
|
+
const key = settings?.openaiKey;
|
|
858
|
+
if (!key) return null;
|
|
859
|
+
return new OpenAI({ apiKey: key });
|
|
860
|
+
}
|
|
837
861
|
const embed = ({ strapi }) => ({
|
|
838
862
|
async generateEmbedding(text) {
|
|
839
863
|
try {
|
|
840
|
-
|
|
864
|
+
const openai = await getOpenAI(strapi);
|
|
865
|
+
if (!openai) {
|
|
866
|
+
strapi.log.warn("OpenAI key missing – skipping embedding");
|
|
841
867
|
return null;
|
|
842
868
|
}
|
|
843
869
|
const response = await openai.embeddings.create({
|
package/package.json
CHANGED