Compare commits

...

2 Commits

Author SHA1 Message Date
Stef Dunlap 0a726b18c8 Fixup merge 2023-03-07 21:44:07 -05:00
Stef Dunlap daa97f9d86 Add context, !chat clears the context !cont keeps it 2023-03-07 21:41:33 -05:00
1 changed files with 69 additions and 23 deletions

View File

@ -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,26 +85,23 @@ 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');
// parse if starts with data: // parse if starts with data:
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
const lines = chunk.split(/\r?\n/); // split by new lines const lines = chunk.split(/\r?\n/); // split by new lines
@ -60,26 +109,23 @@ 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);
console.log(part); console.log(part);