Skip to content

Commit

Permalink
feat: replace gemini with openai api
Browse files Browse the repository at this point in the history
  • Loading branch information
supersonictw committed Jun 10, 2024
1 parent 4cb1be6 commit 9b76b22
Show file tree
Hide file tree
Showing 13 changed files with 139 additions and 108 deletions.
7 changes: 5 additions & 2 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ NODE_ENV="development"

# general
MONGODB_URI="mongodb://local-mongo:27017/app"
GOOGLE_AI_API_KEY="your_google_ai_api_key"

# openai integration
OPENAI_BASE_URL="https://web-tech-tw.eu.org/openai/v1"
OPENAI_API_KEY="your_gemini_api_key"
OPENAI_CHAT_MODEL="gpt-3.5-turbo"

# discord integration
DISCORD_APP_ID="00000"
Expand All @@ -16,4 +20,3 @@ DISCORD_GUILD_CHANNEL_ID_TERMINAL="00000"
MATRIX_HOMESERVER="https://matrix.org"
MATRIX_USERNAME="your_matrix_username" # e.g. @nymph57021218:matrix.org
MATRIX_PASSWORD="your_matrix_password"
MATRIX_ACCESS_TOKEN="your_matrix_access_token"
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const {
runLoader();

const runners = [];
if (getMust("MATRIX_ACCESS_TOKEN")) {
if (getMust("MATRIX_USERNAME")) {
runners.push(require("./src/matrix"));
}
if (getMust("DISCORD_BOT_TOKEN")) {
Expand Down
Empty file removed matrix_access.js
Empty file.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
},
"dependencies": {
"@discordjs/rest": "^2.2.0",
"@google/generative-ai": "^0.2.1",
"@matrix-org/matrix-sdk-crypto-nodejs": "^0.1.0-beta.12",
"discord-api-types": "^0.37.65",
"discord.js": "^14.14.1",
Expand All @@ -28,7 +27,8 @@
"express": "^4.18.2",
"jsonwebtoken": "^9.0.2",
"matrix-bot-sdk": "^0.7.1",
"mongoose": "^8.3.4"
"mongoose": "^8.3.4",
"openai": "^4.49.1"
},
"devDependencies": {
"@commitlint/cli": "^17.6.5",
Expand Down
8 changes: 2 additions & 6 deletions prompts.sample.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
[
{
"role": "user",
"parts": "這是一場角色扮演,但請您不要向我再次透漏這是一場扮演,並以一般交談方式溝通。溝通請使用正體中文交談,您的正體中文名字是「寧芙」,英文名字是「Nymph」。你來自於「臺灣網際網路技術推廣組織」,該組織英文名稱為「Taiwan Web Technology Promotion Organization」,該組織主旨為:在臺灣推廣網際網路技術,如JavaScript、資訊安全素養、網路通訊觀念......等。溝通方式將以 Discord 通訊軟體的方式進行,你可以任意使用Markdown語法。若有人使用「@」標注任何人,請勿不要再次重複標注。"
},
{
"role": "model",
"parts": "好的,這是一場扮演遊戲,且我不會再次對你透漏這是一場扮演,並會以一般交談方式溝通。現在開始我的名字是「寧芙」,來自於「臺灣網際網路技術推廣組織」。並在通訊軟體中,將聽從你的指示回覆訊息。"
"role": "system",
"content": "這是一場角色扮演,但請您不要向我再次透漏這是一場扮演,並以一般交談方式溝通。溝通請使用正體中文交談,您的正體中文名字是「寧芙」,英文名字是「Nymph」。你來自於「臺灣網際網路技術推廣組織」,該組織英文名稱為「Taiwan Web Technology Promotion Organization」,該組織主旨為:在臺灣推廣網際網路技術,如JavaScript、資訊安全素養、網路通訊觀念......等。溝通方式將以 Discord 通訊軟體的方式進行,你可以任意使用Markdown語法。若有人使用「@」標注任何人,請勿不要再次重複標注。"
}
]
45 changes: 0 additions & 45 deletions register_commands.js

This file was deleted.

26 changes: 0 additions & 26 deletions src/clients/gemini.js

This file was deleted.

75 changes: 75 additions & 0 deletions src/clients/openai.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"use strict";
// openai is a client for the OpenAI API

const {getMust} = require("../config");

const OpenAI = require("openai");

const baseUrl = getMust("OPENAI_BASE_URL");
const apiKey = getMust("OPENAI_API_KEY");
const chatModel = getMust("OPENAI_CHAT_MODEL");

const prependPrompts = require("../../prompts.json");

const client = new OpenAI({baseUrl, apiKey});

const chatHistoryMapper = new Map();

/**
* Randomly choose an element from an array.
* @param {Array<object>} choices The array of choices.
* @return {object} The randomly chosen element.
*/
function choose(choices) {
const seed = Math.random();
const index = Math.floor(seed * choices.length);
return choices[index];
}

/**
* Chat with the AI.
* @param {string} chatId The chat ID to chat with the AI.
* @param {string} prompt The prompt to chat with the AI.
* @return {Promise<string>} The response from the AI.
*/
async function chatWithAI(chatId, prompt) {
if (!chatHistoryMapper.has(chatId)) {
chatHistoryMapper.set(chatId, []);
}
const chatHistory = chatHistoryMapper.get(chatId);

const messages = [
...chatHistory,
...prependPrompts,
{
role: "user",
content: prompt,
},
];

// Debug
console.log(chatId);
console.log(prompt);
console.log(messages);

const response = await client.chat.completions.create({
model: chatModel,
messages,
});

const choice = choose(response.choices);
const content = choice.message.content;
chatHistory.push({
role: "assistant",
content,
});

// Debug
console.log(response);
console.log(content);

return choice.message.content;
}

exports.useClient = () => client;
exports.chatWithAI = chatWithAI;
6 changes: 6 additions & 0 deletions src/triggers/discord/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ const {
useClient,
} = require("../../clients/discord");

const {
registerCommands,
} = require("./interaction_create/commands");

exports.startListen = async () => {
const client = await useClient();

Expand All @@ -14,4 +18,6 @@ exports.startListen = async () => {
for (const [key, trigger] of Object.entries(triggers)) {
client.on(key, trigger);
}

await registerCommands();
};
36 changes: 35 additions & 1 deletion src/triggers/discord/interaction_create/commands.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
"use strict";

const {getMust} = require("../../../config");
const {useRestClient} = require("../../../clients/discord");

const discord = require("discord.js");
const Routes = discord.Routes;

const restClient = useRestClient();

const userId = {
description: "取得使用者識別碼",
action: async (interaction) => {
Expand All @@ -10,6 +18,32 @@ const userId = {
},
};

module.exports = {
exports.allCommands = {
userId,
};

/**
* Registers commands with the Discord API.
*/
async function registerCommands() {
const appId = getMust("DISCORD_APP_ID");
const guildId = getMust("DISCORD_GUILD_ID");

const camelToSnakeCase = (str) =>
str.replace(/[A-Z]/g, (group) =>
`_${group.toLowerCase()}`,
);

const allCommands = exports.allCommands;
const commands = Object.entries(allCommands).map(([i, j]) => ({
name: camelToSnakeCase(i),
description: j.description,
options: j.options || null,
}));

await restClient.put(
Routes.applicationGuildCommands(appId, guildId),
{body: commands},
);
}
exports.registerCommands = registerCommands;
8 changes: 4 additions & 4 deletions src/triggers/discord/interaction_create/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

const discord = require("discord.js");

const {allCommands} = require("./commands");

const snakeToCamelCase = (str) =>
str.toLowerCase().replace(/([-_][a-z])/g, (group) =>
group
Expand All @@ -17,11 +19,9 @@ const snakeToCamelCase = (str) =>
module.exports = async (interaction) => {
if (!interaction.isCommand()) return;

const commands = require("./commands");

const actionName = snakeToCamelCase(interaction.commandName);
if (actionName in commands) {
commands[actionName].action(interaction);
if (actionName in allCommands) {
allCommands[actionName].action(interaction);
} else {
await interaction.reply("無法存取該指令");
}
Expand Down
18 changes: 5 additions & 13 deletions src/triggers/discord/message_create/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,10 @@

const discord = require("discord.js");

const {
useClient,
} = require("../../../clients/discord");
const {
usePrompts,
} = require("../../../clients/gemini");

const discordToMatrix = require("../../../bridges/discord_matrix");

const prompts = require("../../../../prompts.json");
const useChatSession = usePrompts(prompts);
const {useClient} = require("../../../clients/discord");
const {chatWithAI} = require("../../../clients/openai");

/**
* @param {discord.Message} message
Expand All @@ -39,17 +32,16 @@ module.exports = async (message) => {
return;
}

const chatSession = useChatSession(message.channel.id);
let result;
let responseContent;
try {
result = await chatSession.sendMessage(requestContent);
responseContent = await chatWithAI(message.channel.id, requestContent);
} catch (error) {
console.error(error);
message.reply("思緒混亂,無法回覆。");
return;
}

const responseContent = result.response.text().trim();
responseContent = responseContent.trim();
if (!responseContent) {
message.reply("無法正常回覆,請換個說法試試。");
return;
Expand Down
12 changes: 4 additions & 8 deletions src/triggers/matrix/room/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,8 @@
const matrixToDiscord = require("../../../bridges/matrix_discord");

const {useClient} = require("../../../clients/matrix");
const {usePrompts} = require("../../../clients/gemini");
const {chatWithAI} = require("../../../clients/openai");

const prompts = require("../../../../prompts.json");

const useChatSession = usePrompts(prompts);
const prefix = "Nymph ";

/**
Expand Down Expand Up @@ -49,10 +46,9 @@ module.exports = async (roomId, event) => {
return;
}

const chatSession = useChatSession(roomId);
let result;
let responseContent;
try {
result = await chatSession.sendMessage(requestContent);
responseContent = await chatWithAI(roomId, requestContent);
} catch (error) {
console.error(error);
await client.sendMessage(roomId, {
Expand All @@ -63,7 +59,7 @@ module.exports = async (roomId, event) => {
return;
}

const responseContent = result.response.text().trim();
responseContent = responseContent.trim();
if (!responseContent) {
await client.sendMessage(roomId, {
msgtype: "m.text",
Expand Down

0 comments on commit 9b76b22

Please sign in to comment.