-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathindex.mjs
112 lines (101 loc) · 3.46 KB
/
index.mjs
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
102
103
104
105
106
107
108
109
110
111
112
import env from "dotenv";
env.config();
import fs from "fs";
import { Parser } from "expr-eval";
import * as readline from "node:readline/promises";
import { stdin as input, stdout as output } from "node:process";
const rl = readline.createInterface({ input, output });
const promptTemplate = fs.readFileSync("prompt.txt", "utf8");
const mergeTemplate = fs.readFileSync("merge.txt", "utf8");
// use serpapi to answer the question
const googleSearch = async (question) =>
await fetch(
`https://serpapi.com/search?api_key=${process.env.SERPAPI_API_KEY}&q=${question}`
)
.then((res) => res.json())
.then(
(res) =>
// try to pull the answer from various components of the response
res.answer_box?.answer ||
res.answer_box?.snippet ||
res.organic_results?.[0]?.snippet
);
// tools that can be used to answer questions
const tools = {
search: {
description:
"a search engine. useful for when you need to answer questions about current events. input should be a search query.",
execute: googleSearch,
},
calculator: {
description:
"Useful for getting the result of a math expression. The input to this tool should be a valid mathematical expression that could be executed by a simple calculator.",
execute: (input) => Parser.evaluate(input).toString(),
},
};
// use GPT-3.5 to complete a given prompts
const completePrompt = async (prompt) =>
await fetch("https://api.openai.com/v1/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + process.env.OPENAI_API_KEY,
},
body: JSON.stringify({
model: "text-davinci-003",
prompt,
max_tokens: 256,
temperature: 0.7,
stream: false,
stop: ["Observation:"],
}),
})
.then((res) => res.json())
.then((res) => res.choices[0].text)
.then((res) => {
console.log("\x1b[91m" + prompt + "\x1b[0m");
console.log("\x1b[92m" + res + "\x1b[0m");
return res;
});
const answerQuestion = async (question) => {
// construct the prompt, with our question and the tools that the chain can use
let prompt = promptTemplate.replace("${question}", question).replace(
"${tools}",
Object.keys(tools)
.map((toolname) => `${toolname}: ${tools[toolname].description}`)
.join("\n")
);
// allow the LLM to iterate until it finds a final answer
while (true) {
const response = await completePrompt(prompt);
// add this to the prompt
prompt += response;
const action = response.match(/Action: (.*)/)?.[1];
if (action) {
// execute the action specified by the LLMs
const actionInput = response.match(/Action Input: "?(.*)"?/)?.[1];
const result = await tools[action.trim()].execute(actionInput);
prompt += `Observation: ${result}\n`;
} else {
return response.match(/Final Answer: (.*)/)?.[1];
}
}
};
// merge the chat history with a new question
const mergeHistory = async (question, history) => {
const prompt = mergeTemplate
.replace("${question}", question)
.replace("${history}", history);
return await completePrompt(prompt);
};
// main loop - answer the user's questions
let history = "";
while (true) {
let question = await rl.question("How can I help? ");
if (history.length > 0) {
question = await mergeHistory(question, history);
}
const answer = await answerQuestion(question);
console.log(answer);
history += `Q:${question}\nA:${answer}\n`;
}