Compare commits
2 Commits
cb42f77ed2
...
0a726b18c8
Author | SHA1 | Date |
---|---|---|
Stef Dunlap | 0a726b18c8 | |
Stef Dunlap | daa97f9d86 |
82
index.js
82
index.js
|
@ -3,13 +3,62 @@
|
||||||
const irc = require('irc');
|
const irc = require('irc');
|
||||||
const axios = require('axios');
|
const axios = require('axios');
|
||||||
const config = require('./config.json');
|
const config = require('./config.json');
|
||||||
|
|
||||||
|
// context is a list of strings that are used to seed the chatgpt api and it's responses
|
||||||
|
class Context {
|
||||||
|
messages = [];
|
||||||
|
currentResponse = '';
|
||||||
|
|
||||||
|
add_user_message(message) {
|
||||||
|
this.messages.push({ role: 'user', content: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
add_assistant_message(message) {
|
||||||
|
this.messages.push({ role: 'assistant', content: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
append_current_response(message) {
|
||||||
|
this.currentResponse += message;
|
||||||
|
}
|
||||||
|
|
||||||
|
end_line() {
|
||||||
|
const response_so_far = this.currentResponse;
|
||||||
|
this.currentResponse += "\n\n"
|
||||||
|
return response_so_far;
|
||||||
|
}
|
||||||
|
|
||||||
|
finish_current_response() {
|
||||||
|
this.add_assistant_message(this.currentResponse);
|
||||||
|
const the_response = this.currentResponse;
|
||||||
|
this.currentResponse = '';
|
||||||
|
return the_response;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_response_in_progress() {
|
||||||
|
return this.currentResponse !== '';
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.messages = [];
|
||||||
|
this.currentResponse = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = new Context();
|
||||||
|
|
||||||
const client = new irc.Client(config.server, config.nick, {
|
const client = new irc.Client(config.server, config.nick, {
|
||||||
channels: config.channels,
|
channels: config.channels,
|
||||||
});
|
});
|
||||||
|
|
||||||
// listen for messages that start with !chat and call the chatgpt api with a callback that prints the response line by line
|
// listen for messages that start with !chat and call the chatgpt api with a callback that prints the response line by line
|
||||||
client.addListener('message', async (from, to, message) => {
|
client.addListener('message', async (from, to, message) => {
|
||||||
if (message.startsWith('!chat')) {
|
is_chat_cmd = message.startsWith('!chat');
|
||||||
|
is_cont_cmd = message.startsWith('!cont');
|
||||||
|
if (is_chat_cmd || is_cont_cmd) {
|
||||||
|
if(context.is_response_in_progress()) { return; }
|
||||||
|
if(is_chat_cmd) {
|
||||||
|
context.clear();
|
||||||
|
}
|
||||||
const query = message.slice(6);
|
const query = message.slice(6);
|
||||||
chatgpt(query, (line) => {
|
chatgpt(query, (line) => {
|
||||||
client.say(to, line);
|
client.say(to, line);
|
||||||
|
@ -19,10 +68,13 @@ client.addListener('message', async (from, to, message) => {
|
||||||
|
|
||||||
// function that calls the chatgpt streaming api (with server send events) and calls the callback function for each line
|
// function that calls the chatgpt streaming api (with server send events) and calls the callback function for each line
|
||||||
async function chatgpt(query, callback) {
|
async function chatgpt(query, callback) {
|
||||||
|
// a very primitive mutex to prevent multiple calls to the api at once
|
||||||
|
if(context.is_response_in_progress()) { return; }
|
||||||
|
context.add_user_message(query);
|
||||||
const apiUrl = 'https://api.openai.com/v1/chat/completions';
|
const apiUrl = 'https://api.openai.com/v1/chat/completions';
|
||||||
|
|
||||||
const response = await axios.post(apiUrl, {
|
const response = await axios.post(apiUrl, {
|
||||||
messages: [{ role: 'user', content: query }],
|
messages: [context.messages],
|
||||||
model: 'gpt-3.5-turbo',
|
model: 'gpt-3.5-turbo',
|
||||||
stream: true,
|
stream: true,
|
||||||
}, {
|
}, {
|
||||||
|
@ -33,7 +85,6 @@ async function chatgpt(query, callback) {
|
||||||
responseType: 'stream',
|
responseType: 'stream',
|
||||||
});
|
});
|
||||||
|
|
||||||
let line = '';
|
|
||||||
response.data.on('data', (event) => {
|
response.data.on('data', (event) => {
|
||||||
let data = event.toString();
|
let data = event.toString();
|
||||||
let parts = data.split('\n');
|
let parts = data.split('\n');
|
||||||
|
@ -41,16 +92,14 @@ async function chatgpt(query, callback) {
|
||||||
for(part of parts) {
|
for(part of parts) {
|
||||||
console.log(part);
|
console.log(part);
|
||||||
if(part === 'data: [DONE]') {
|
if(part === 'data: [DONE]') {
|
||||||
callback(line);
|
callback(context.finish_current_response());
|
||||||
line = '';
|
|
||||||
} else if(part.startsWith('data: ')) {
|
} else if(part.startsWith('data: ')) {
|
||||||
let jsonString = part.slice(part.indexOf('{'), part.lastIndexOf('}') + 1);
|
let jsonString = part.slice(part.indexOf('{'), part.lastIndexOf('}') + 1);
|
||||||
try {
|
try {
|
||||||
let json = JSON.parse(jsonString);
|
let json = JSON.parse(jsonString);
|
||||||
let chunk = json.choices[0].delta.content;
|
let chunk = json.choices[0].delta.content;
|
||||||
if(!chunk) {
|
if (!chunk) {
|
||||||
callback(line);
|
callback(context.end_line());
|
||||||
line = '';
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//split the chunk into lines leaving the delimiter in the array
|
//split the chunk into lines leaving the delimiter in the array
|
||||||
|
@ -60,25 +109,22 @@ async function chatgpt(query, callback) {
|
||||||
let hasEndNewline = chunk.endsWith("\n");
|
let hasEndNewline = chunk.endsWith("\n");
|
||||||
|
|
||||||
if(hasStartNewline) {
|
if(hasStartNewline) {
|
||||||
callback(line);
|
callback(context.end_line())
|
||||||
line = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < lines.length - 1; i++) {
|
for (let i = 0; i < lines.length - 1; i++) {
|
||||||
callback(line+lines[i]);
|
context.append_current_response(lines[i]);
|
||||||
line = '';
|
callback(context.end_line());
|
||||||
}
|
}
|
||||||
|
|
||||||
line += lines[lines.length - 1];
|
context.append_current_response(lines[lines.length - 1]);
|
||||||
|
|
||||||
if(hasEndNewline) {
|
if(hasEndNewline) {
|
||||||
callback(line);
|
callback(context.end_line());
|
||||||
line = '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(line.length > 400) {
|
if (line.length > 400) {
|
||||||
callback(line);
|
callback(context.end_line());
|
||||||
line = '';
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
|
|
Loading…
Reference in New Issue