Skip to content

Commit

Permalink
Merge pull request #18 from freemocap/jon/cleanup
Browse files Browse the repository at this point in the history
Discord UI/UX improvements and general cleanup
  • Loading branch information
jonmatthis authored Jan 15, 2024
2 parents e4ecd7c + de296fd commit 08870b9
Show file tree
Hide file tree
Showing 7 changed files with 347 additions and 257 deletions.
2 changes: 1 addition & 1 deletion src/core/ai/langchain/langchain.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class LangchainService {
const model = await this._createModel(modelName);
const contextInstructionsOrAtLeastBeChill =
contextInstructions ||
'I keep my answers short (1-2 sentences) unless there is a reason to say more.';
'I keep my answers short unless there is a reason to say more.';
const prompt = ChatPromptTemplate.fromMessages([
[
'system',
Expand Down
28 changes: 14 additions & 14 deletions src/core/bot/bot.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,44 +83,44 @@ export class BotService {
...additionalArgs,
});

let streamedResult = '';
let fullStreamedResult = '';
let subStreamResult = '';
let didResetOccur = false;
let tokens = 0;
let tokensInThisChunk = 0;
const chunkSize = 10;
for await (const chunk of chatStream) {
for await (const newToken of chatStream) {
// the full message
streamedResult += chunk;
tokens++;
fullStreamedResult += newToken;
tokensInThisChunk++;
if (subStreamResult.length < splitAt) {
subStreamResult += chunk;
subStreamResult += newToken;
} else {
//
subStreamResult = subStreamResult.slice(subStreamResult.length * 0.95);
subStreamResult += chunk;
subStreamResult = subStreamResult.slice(subStreamResult.length * 0.9);
subStreamResult += newToken;
didResetOccur = true;
}

if (tokens === chunkSize) {
this._logger.log(`Streaming chunk of data: ${subStreamResult}`);
if (tokensInThisChunk === chunkSize) {
this._logger.debug(`Streaming chunk of data: ${subStreamResult}`);
yield {
data: streamedResult,
data: fullStreamedResult,
theChunk: subStreamResult,
didResetOccur,
};
tokens = 0;
tokensInThisChunk = 0;
didResetOccur = false;
}
}
this._logger.log('Stream complete');
yield {
data: streamedResult,
data: fullStreamedResult,
theChunk: subStreamResult,
};

chatbot.memory.saveContext(
{ input: humanMessage },
{ output: streamedResult },
{ output: fullStreamedResult },
);
}
}
6 changes: 5 additions & 1 deletion src/interfaces/discord/discord.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import { AiChatsModule } from '../../core/database/collections/ai-chats/ai-chats
import { CoupletsModule } from '../../core/database/collections/couplets/couplets.module';
import { MessagesModule } from '../../core/database/collections/messages/messages.module';
import { DiscordContextService } from './services/discord-context.service';
import { DiscordPersistenceService } from './services/discord-persistence.service';
import { DiscordMessageService } from './services/discord-message.service';

@Module({
imports: [
Expand All @@ -27,10 +29,12 @@ import { DiscordContextService } from './services/discord-context.service';
MessagesModule,
],
providers: [
DiscordReadyService,
DiscordPingService,
DiscordThreadService,
DiscordReadyService,
DiscordMessageService,
DiscordContextService,
DiscordPersistenceService,
Logger,
],
})
Expand Down
176 changes: 89 additions & 87 deletions src/interfaces/discord/services/discord-context.service.ts
Original file line number Diff line number Diff line change
@@ -1,108 +1,110 @@
import { Injectable } from '@nestjs/common';
import { ReactionEmoji, TextChannel, ThreadChannel } from 'discord.js';
import { Injectable, Logger } from '@nestjs/common';
import {
Message,
ReactionEmoji,
TextChannel,
ThreadChannel,
ChannelType,
Guild,
GuildEmoji,
} from 'discord.js';
import { DiscordContextRouteFactory } from '../../../core/database/collections/ai-chats/context-route.provider';

@Injectable()
export class DiscordContextService {
private readonly logger = new Logger(DiscordContextService.name);
instructionsChannelPattern = new RegExp('bot-instructions.*', 'i');

getContextRoute(channel: TextChannel, thread?: ThreadChannel) {
// TODO - Handle Direct Messages
return DiscordContextRouteFactory.create(
false,
{
type: 'channel',
contextId: channel.id,
contextName: channel.name,
},
{
type: 'server',
contextId: channel.guild?.id,
contextName: channel.guild?.name,
},
{
type: 'category',
contextId: channel?.parentId,
contextName: channel.parent?.name,
},
{
type: 'thread',
contextId: thread?.id,
contextName: thread?.name,
},
);
try {
return DiscordContextRouteFactory.create(
false,
{
type: 'channel',
contextId: channel.id,
contextName: channel.name,
},
{
type: 'server',
contextId: channel.guild?.id,
contextName: channel.guild?.name,
},
{
type: 'category',
contextId: channel?.parentId,
contextName: channel.parent?.name,
},
{
type: 'thread',
contextId: thread?.id,
contextName: thread?.name,
},
);
} catch (error) {
this.logger.error(`Failed to get context route: ${error.message}`);
throw error;
}
}

async getContextInstructions(channel: TextChannel) {
const channelInstructions = channel.topic || '';
const categoryInstructions = await this.getCategoryInstructions(channel);
const serverInstructions = await this.getServerInstructions(channel);
return [serverInstructions, categoryInstructions, channelInstructions].join(
'\n\n',
);
}

private async getCategoryInstructions(channel: TextChannel): Promise<string> {
const category = channel.parent;
if (!category) {
return '';
try {
const server = await channel.client.guilds.fetch(channel.guildId);
const channelInstructions = channel.topic || '';
const categoryInstructions = await this.getInstructions(
server,
channel.parentId,
);
const serverInstructions = await this.getInstructions(server, null);
return [
serverInstructions,
categoryInstructions,
channelInstructions,
].join('\n\n');
} catch (error) {
this.logger.error(
`Failed to get context instructions: ${error.message} - returning empty string!`,
);
throw error;
}
const categoryChannels = channel.guild.channels.cache.filter(
(ch) => ch.parentId === channel.parentId,
);

const botConfigChannel = categoryChannels.find((ch) =>
/bot-config.*?/i.test(ch.name),
) as TextChannel | undefined;
if (!botConfigChannel) {
return '';
}

const messages = await botConfigChannel.messages.fetch({ limit: 100 });

const instructionMessages = messages.filter((message) =>
message.reactions.cache.some((reaction) =>
// @ts-ignore
this.isBotInstructionEmoji(reaction.emoji),
),
);

const instructions = instructionMessages
.map((message) => message.content)
.join('\n');

return instructions;
}

private async getServerInstructions(channel: TextChannel): Promise<string> {
if (!channel.guild) {
return '';
}
const topLevelChannels = channel.guild.channels.cache.filter(
(ch) => !ch.parent, // only get channels that are not in a category
);
private async getInstructions(
server: Guild,
categoryId: string | null,
): Promise<string> {
try {
const channels = server.channels.cache.filter(
(ch) => ch.parentId === categoryId && ch.type === ChannelType.GuildText,
);

const botConfigChannel = topLevelChannels.find((ch) =>
/bot-config.*?/i.test(ch.name),
) as TextChannel | undefined;
if (!botConfigChannel) {
return '';
}
const botConfigChannel = channels.find((ch) =>
this.instructionsChannelPattern.test(ch.name),
) as TextChannel | undefined;

const messages = await botConfigChannel.messages.fetch({ limit: 100 });
if (!botConfigChannel) {
return '';
}

const instructionMessages = messages.filter((message) =>
message.reactions.cache.some((reaction) =>
// @ts-ignore
this.isBotInstructionEmoji(reaction.emoji),
),
);
const messages = await botConfigChannel.messages.fetch({ limit: 100 });

const instructions = instructionMessages
.map((message) => message.content)
.join('\n');
const instructionMessages = messages.filter((message: Message) =>
message.reactions.cache.some((reaction) =>
this.isBotInstructionEmoji(reaction.emoji),
),
);

return instructions;
return instructionMessages
.map((message: Message) => message.content)
.join('\n');
} catch (error) {
this.logger.error(`Failed to get instructions: ${error.message}`);
return ''; // In case of an error, return an empty string to keep the bot operational.
}
}
private isBotInstructionEmoji(emoji: ReactionEmoji): boolean {

private isBotInstructionEmoji(emoji: GuildEmoji | ReactionEmoji): boolean {
// Ensure emoji.name is not `null` before comparison
return emoji.name === '🤖';
}
}
Loading

0 comments on commit 08870b9

Please sign in to comment.