-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathchatbot.js
101 lines (85 loc) · 2.57 KB
/
chatbot.js
1
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
'use strict';
const Article = require('./src/db/article');
const { Configuration, OpenAIApi } = require('openai');
const assert = require('assert');
const axios = require('axios');
const similarity = require('compute-cosine-similarity');
const apiKey = process.env.OPEN_AI_KEY;
assert.ok(apiKey, 'No OPEN_AI_KEY specified');
const configuration = new Configuration({
apiKey
});
const openai = new OpenAIApi(configuration);
module.exports = async function chatbot(req, res) {
const { question } = req.body;
const embedding = await createEmbedding(question).catch(err => {
throw err;
});
let articles = await Article
.find()
.select({ $similarity: 1, $vector: 1, title: 1, content: 1, url: 1 })
.sort({ $vector: { $meta: embedding } })
.limit(10);
articles = mmr(articles, embedding).slice(0, 3);
const prompt = `Answer this question with this context:\n\nQuestion: ${question}\n\nContext: ${articles[0].content}\n\nContext: ${articles[1].content}\n\nContext: ${articles[2].content}`;
const response = await openai.createChatCompletion({
model: 'gpt-3.5-turbo',
messages: [
{
role: 'user',
content: prompt
}
],
temperature: 0,
max_tokens: 2000
});
res.json({
content: response.data.choices[0].message.content,
link: articles[0].url,
title: articles[0].title,
sources: articles.map(article => ({ link: article.url, title: article.title }))
});
}
function createEmbedding(input) {
return axios({
method: 'POST',
url: 'https://api.openai.com/v1/embeddings',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${apiKey}`
},
data: {
model: 'text-embedding-ada-002',
input
}
}).then(res => res.data.data[0].embedding);
}
function mmr(docs, embedding) {
// Result set
const s = [];
// Original phrases
const r = [...docs];
const lambda = 0.7;
let score = 0;
while (r.length > 0) {
let docToAdd = 0;
for (let i = 0; i < r.length; ++i) {
const originalSimilarity = similarity(embedding, r[i].$vector);
let maxDistance = 0;
for (let j = 0; j < s.length; ++j) {
const similarityToCurrent = similarity(r[i].$vector, s[j].$vector);
if (similarityToCurrent > maxDistance) {
maxDistance = similarityToCurrent;
}
}
const equationScore = lambda * originalSimilarity - (1 - lambda) * maxDistance;
if (equationScore > score) {
score = equationScore;
docToAdd = i;
}
}
const [doc] = r.splice(docToAdd, 1);
s.push(doc);
}
return s;
}