Compare commits
No commits in common. "0b8391a7e0fe5bcc5ea6a0af2ad5cbd3341d4838" and "902076cd286d53b61ffab7ef6bbbbfecfe2cfe80" have entirely different histories.
0b8391a7e0
...
902076cd28
|
@ -34,4 +34,3 @@ ALLOWED_CHAT=
|
||||||
|
|
||||||
SAFE_WORD=
|
SAFE_WORD=
|
||||||
# When this character/string is detected anywhere in a message, the bot won't respond to it. Defaults to "\".
|
# When this character/string is detected anywhere in a message, the bot won't respond to it. Defaults to "\".
|
||||||
|
|
||||||
|
|
65
src/bot.ts
65
src/bot.ts
|
@ -1,7 +1,7 @@
|
||||||
import { TailchatWsClient } from "tailchat-client-sdk";
|
import { TailchatWsClient, stripMentionTag } from "tailchat-client-sdk";
|
||||||
import { OpenAI } from "openai";
|
import { OpenAI } from "openai";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import { GuildData, ImageRequestData, ImageSize } from "./types";
|
import { ImageRequestData, ImageSize, Messages } from "./types";
|
||||||
import {
|
import {
|
||||||
checkFile,
|
checkFile,
|
||||||
getUsername,
|
getUsername,
|
||||||
|
@ -20,6 +20,7 @@ import {
|
||||||
createImageModel,
|
createImageModel,
|
||||||
temperature,
|
temperature,
|
||||||
key,
|
key,
|
||||||
|
system,
|
||||||
} from "./assistant";
|
} from "./assistant";
|
||||||
import { ImagesResponse } from "openai/resources";
|
import { ImagesResponse } from "openai/resources";
|
||||||
import chalk from "chalk";
|
import chalk from "chalk";
|
||||||
|
@ -27,9 +28,9 @@ import dotenv from "dotenv";
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
// Specific to Tailchat. The endpoint of my Tailchat server, the bot ID and Secret.
|
// Specific to Tailchat. The endpoint of my Tailchat server, the bot ID and Secret.
|
||||||
export const HOST = process.env.HOST;
|
const HOST = process.env.HOST;
|
||||||
export const APPID = process.env.ID;
|
const APPID = process.env.ID;
|
||||||
export const APPSECRET = process.env.SECRET;
|
const APPSECRET = process.env.SECRET;
|
||||||
const allVarsFilled = HOST && APPID && APPSECRET;
|
const allVarsFilled = HOST && APPID && APPSECRET;
|
||||||
|
|
||||||
if (!allVarsFilled) {
|
if (!allVarsFilled) {
|
||||||
|
@ -38,13 +39,8 @@ if (!allVarsFilled) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Define the initial system message for the LLM.
|
// Define the initial system message for the LLM.
|
||||||
const session = new GuildData(checkFile("./messages.json", "utf-8"));
|
const session: Messages = checkFile("./messages.json", "utf-8", system.normal);
|
||||||
|
console.log("Our conversation is:", session);
|
||||||
session.data.toString()
|
|
||||||
? console.log("Our conversation is:", session.data)
|
|
||||||
: console.log(
|
|
||||||
"Looks like we're starting fresh, no previous chat history was found.",
|
|
||||||
);
|
|
||||||
|
|
||||||
const THINKING = "[md]`Thinking...`[/md]";
|
const THINKING = "[md]`Thinking...`[/md]";
|
||||||
|
|
||||||
|
@ -121,13 +117,10 @@ client.connect().then(async () => {
|
||||||
content: "[md]`Analyzing image...`[/md]",
|
content: "[md]`Analyzing image...`[/md]",
|
||||||
});
|
});
|
||||||
|
|
||||||
session.appendMessage(
|
session.push(formatImageMessage(username, imageData));
|
||||||
formatImageMessage(username, imageData),
|
|
||||||
message.converseId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await assistant.chat.completions.create({
|
const response = await assistant.chat.completions.create({
|
||||||
messages: session.getHistory(message.converseId),
|
messages: session,
|
||||||
model: imageModel,
|
model: imageModel,
|
||||||
temperature: temperature,
|
temperature: temperature,
|
||||||
});
|
});
|
||||||
|
@ -136,7 +129,7 @@ client.connect().then(async () => {
|
||||||
"",
|
"",
|
||||||
);
|
);
|
||||||
|
|
||||||
session.appendMessage(messageOf(response), message.converseId);
|
session.push(messageOf(response));
|
||||||
|
|
||||||
await client.sendMessage({
|
await client.sendMessage({
|
||||||
converseId: message.converseId,
|
converseId: message.converseId,
|
||||||
|
@ -145,11 +138,7 @@ client.connect().then(async () => {
|
||||||
content: `[md]${contentOf(response)}[/md]`,
|
content: `[md]${contentOf(response)}[/md]`,
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync("./messages.json", JSON.stringify(session), "utf8");
|
||||||
"./messages.json",
|
|
||||||
JSON.stringify(session.data),
|
|
||||||
"utf8",
|
|
||||||
);
|
|
||||||
console.log("Now our conversation is", session);
|
console.log("Now our conversation is", session);
|
||||||
} else {
|
} else {
|
||||||
const username = await getUsername(HOST, message.author!);
|
const username = await getUsername(HOST, message.author!);
|
||||||
|
@ -160,18 +149,15 @@ client.connect().then(async () => {
|
||||||
content: THINKING,
|
content: THINKING,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.appendMessage(
|
session.push(formatUserMessage(username, message.content));
|
||||||
formatUserMessage(username, message.content),
|
|
||||||
message.converseId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await assistant.chat.completions.create({
|
const response = await assistant.chat.completions.create({
|
||||||
messages: session.getHistory(message.converseId),
|
messages: session,
|
||||||
model: textModel,
|
model: textModel,
|
||||||
temperature: temperature,
|
temperature: temperature,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.appendMessage(messageOf(response), message.converseId);
|
session.push(messageOf(response));
|
||||||
|
|
||||||
await client.sendMessage({
|
await client.sendMessage({
|
||||||
converseId: message.converseId,
|
converseId: message.converseId,
|
||||||
|
@ -180,11 +166,7 @@ client.connect().then(async () => {
|
||||||
content: `[md]${contentOf(response)}[/md]`,
|
content: `[md]${contentOf(response)}[/md]`,
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync("./messages.json", JSON.stringify(session), "utf8");
|
||||||
"./messages.json",
|
|
||||||
JSON.stringify(session.data),
|
|
||||||
"utf8",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed", err);
|
console.log("Failed", err);
|
||||||
|
@ -205,29 +187,22 @@ client.connect().then(async () => {
|
||||||
content: THINKING,
|
content: THINKING,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.appendMessage(
|
session.push(formatUserMessage(username, message.content));
|
||||||
formatUserMessage(username, message.content),
|
|
||||||
message.converseId,
|
|
||||||
);
|
|
||||||
|
|
||||||
const response = await assistant.chat.completions.create({
|
const response = await assistant.chat.completions.create({
|
||||||
messages: session.getHistory(message.converseId),
|
messages: session,
|
||||||
model: textModel,
|
model: textModel,
|
||||||
temperature: temperature,
|
temperature: temperature,
|
||||||
});
|
});
|
||||||
|
|
||||||
session.appendMessage(messageOf(response), message.converseId);
|
session.push(messageOf(response));
|
||||||
|
|
||||||
await client.sendMessage({
|
await client.sendMessage({
|
||||||
converseId: message.converseId,
|
converseId: message.converseId,
|
||||||
content: `[md]${contentOf(response)}[/md]`,
|
content: `[md]${contentOf(response)}[/md]`,
|
||||||
});
|
});
|
||||||
|
|
||||||
fs.writeFileSync(
|
fs.writeFileSync("./messages.json", JSON.stringify(session), "utf8");
|
||||||
"./messages.json",
|
|
||||||
JSON.stringify(session.data),
|
|
||||||
"utf8",
|
|
||||||
);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("Failed", err);
|
console.log("Failed", err);
|
||||||
|
|
||||||
|
|
49
src/types.ts
49
src/types.ts
|
@ -5,10 +5,6 @@ import {
|
||||||
ChatCompletionSystemMessageParam,
|
ChatCompletionSystemMessageParam,
|
||||||
} from "openai/resources";
|
} from "openai/resources";
|
||||||
import { TailchatWsClient } from "tailchat-client-sdk";
|
import { TailchatWsClient } from "tailchat-client-sdk";
|
||||||
import { ChatMessage } from "tailchat-types";
|
|
||||||
import { formatNewHistory, formatUserMessage, getUsername } from "./utils";
|
|
||||||
import { HOST } from "./bot";
|
|
||||||
import chalk from "chalk";
|
|
||||||
|
|
||||||
export interface ImageMessage {
|
export interface ImageMessage {
|
||||||
role: "user";
|
role: "user";
|
||||||
|
@ -31,8 +27,6 @@ export type AnyChatCompletion =
|
||||||
| ChatCompletionUserMessageParam
|
| ChatCompletionUserMessageParam
|
||||||
| ChatCompletionSystemMessageParam;
|
| ChatCompletionSystemMessageParam;
|
||||||
|
|
||||||
export type AnyChatCompletionRole = "user" | "system" | "assistant";
|
|
||||||
|
|
||||||
export enum ImageSize {
|
export enum ImageSize {
|
||||||
Small = "256x256",
|
Small = "256x256",
|
||||||
Medium = "512x512",
|
Medium = "512x512",
|
||||||
|
@ -88,45 +82,4 @@ export type Temperature =
|
||||||
| 1.9
|
| 1.9
|
||||||
| 2.0;
|
| 2.0;
|
||||||
|
|
||||||
export type Messages = (AnyChatCompletion | ImageMessage)[];
|
export type Messages = Array<AnyChatCompletion | ImageMessage>;
|
||||||
|
|
||||||
export interface ChatHistoryData {
|
|
||||||
id: string;
|
|
||||||
history: Messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ChatHistory = ChatHistoryData[];
|
|
||||||
|
|
||||||
export class GuildData {
|
|
||||||
data: ChatHistory;
|
|
||||||
|
|
||||||
constructor(existingData?: ChatHistory) {
|
|
||||||
this.data = existingData || [];
|
|
||||||
}
|
|
||||||
|
|
||||||
appendMessage(
|
|
||||||
message: AnyChatCompletion | ImageMessage,
|
|
||||||
converseId: string,
|
|
||||||
): void {
|
|
||||||
for (const converse of this.data) {
|
|
||||||
if (converseId === converse.id) {
|
|
||||||
converse.history.push(message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.data.push(formatNewHistory(message, converseId));
|
|
||||||
}
|
|
||||||
|
|
||||||
getHistory(converseId: string): Messages {
|
|
||||||
for (const converse of this.data) {
|
|
||||||
if (converseId === converse.id) {
|
|
||||||
return converse.history;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`No history was found with the given converse id: ${chalk.green(converseId)}. Something isn't right.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
83
src/utils.ts
83
src/utils.ts
|
@ -1,70 +1,28 @@
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
import {
|
import { AnyChatCompletion, ImageMessage, Messages } from "./types";
|
||||||
AnyChatCompletion,
|
|
||||||
AnyChatCompletionRole,
|
|
||||||
ChatHistory,
|
|
||||||
ChatHistoryData,
|
|
||||||
ImageMessage,
|
|
||||||
} from "./types";
|
|
||||||
import { system } from "./assistant";
|
|
||||||
import { ChatCompletion, ChatCompletionMessage } from "openai/resources";
|
import { ChatCompletion, ChatCompletionMessage } from "openai/resources";
|
||||||
import { stripMentionTag } from "tailchat-client-sdk";
|
import { stripMentionTag } from "tailchat-client-sdk";
|
||||||
import { ChatMessage } from "tailchat-types";
|
|
||||||
import { HOST } from "./bot";
|
|
||||||
import chalk from "chalk";
|
|
||||||
|
|
||||||
//export function checkFile(
|
|
||||||
// file: string,
|
|
||||||
// encoding: fs.EncodingOption,
|
|
||||||
// defaultContent: string,
|
|
||||||
//): Messages {
|
|
||||||
// let final: Messages;
|
|
||||||
// const generic: Messages = [
|
|
||||||
// {
|
|
||||||
// role: "system",
|
|
||||||
// content: defaultContent,
|
|
||||||
// },
|
|
||||||
// ];
|
|
||||||
//
|
|
||||||
// if (fs.existsSync(file)) {
|
|
||||||
// const data = fs.readFileSync(file, encoding);
|
|
||||||
//
|
|
||||||
// final = !data.toString().trim() ? generic : JSON.parse(data.toString());
|
|
||||||
// } else {
|
|
||||||
// fs.createWriteStream(file);
|
|
||||||
// final = generic;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return final;
|
|
||||||
//}
|
|
||||||
|
|
||||||
export function checkFile(
|
export function checkFile(
|
||||||
file: string,
|
file: string,
|
||||||
encoding: fs.EncodingOption,
|
encoding: fs.EncodingOption,
|
||||||
): ChatHistory {
|
defaultContent: string,
|
||||||
let final: ChatHistory;
|
): Messages {
|
||||||
|
let final: Messages;
|
||||||
|
const generic: Messages = [
|
||||||
|
{
|
||||||
|
role: "system",
|
||||||
|
content: defaultContent,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if (fs.existsSync(file)) {
|
if (fs.existsSync(file)) {
|
||||||
const data = fs.readFileSync(file, encoding);
|
const data = fs.readFileSync(file, encoding);
|
||||||
|
|
||||||
final = !data.toString().trim() ? [] : JSON.parse(data.toString());
|
final = !data.toString().trim() ? generic : JSON.parse(data.toString());
|
||||||
|
|
||||||
try {
|
|
||||||
// @ts-ignore
|
|
||||||
if (final.at(0).role) {
|
|
||||||
console.warn(
|
|
||||||
chalk.yellow(
|
|
||||||
"Your persistent storage uses the old data structure for persistent messages. These messages will be moved to a backup file and the existing file will be overwritten.",
|
|
||||||
),
|
|
||||||
);
|
|
||||||
fs.writeFileSync(`${file}.bak`, data);
|
|
||||||
|
|
||||||
final = [];
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
} else {
|
} else {
|
||||||
fs.createWriteStream(file);
|
fs.createWriteStream(file);
|
||||||
final = [];
|
final = generic;
|
||||||
}
|
}
|
||||||
|
|
||||||
return final;
|
return final;
|
||||||
|
@ -101,23 +59,6 @@ export function formatImageMessage(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function formatNewHistory(
|
|
||||||
message: AnyChatCompletion | ImageMessage,
|
|
||||||
converseId: string,
|
|
||||||
customSystemMessage?: string,
|
|
||||||
): ChatHistoryData {
|
|
||||||
return {
|
|
||||||
id: converseId,
|
|
||||||
history: [
|
|
||||||
{
|
|
||||||
role: "system",
|
|
||||||
content: customSystemMessage || system.normal,
|
|
||||||
},
|
|
||||||
message,
|
|
||||||
],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function messageOf(response: ChatCompletion): ChatCompletionMessage {
|
export function messageOf(response: ChatCompletion): ChatCompletionMessage {
|
||||||
return response.choices.at(0)!.message;
|
return response.choices.at(0)!.message;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue