From 4aadea461179a815772bb0f54e705e13256510e9 Mon Sep 17 00:00:00 2001 From: Kevin Dang <77701718+kevinthedang@users.noreply.github.com> Date: Tue, 2 Jun 2026 15:34:48 -0700 Subject: [PATCH] Summary Command (#194) --- README.md | 2 +- docker-compose.yml | 2 +- package-lock.json | 4 +- package.json | 2 +- src/commands/index.ts | 4 +- src/commands/summary.ts | 50 ++++++++++++++++++++++++ src/utils/handlers/chatHistoryHandler.ts | 15 +++++++ src/utils/messageNormal.ts | 32 +++++++++++++++ tests/commands.test.ts | 2 +- 9 files changed, 106 insertions(+), 7 deletions(-) create mode 100644 src/commands/summary.ts diff --git a/README.md b/README.md index b10ee40..4890ff8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ The project aims to: * [x] Message Persistance * [x] Containerization with Docker * [x] Slash Commands Compatible - * [ ] Summary Command + * [x] Summary Command * [ ] Model Info Command * [ ] List Models Command * [x] Pull Model Command diff --git a/docker-compose.yml b/docker-compose.yml index 8baf88b..344fac9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: build: ./ # find docker file in designated path container_name: discord restart: always # rebuild container always - image: kevinthedang/discord-ollama:0.8.7 + image: kevinthedang/discord-ollama:0.9.0 environment: CLIENT_TOKEN: ${CLIENT_TOKEN} OLLAMA_IP: ${OLLAMA_IP} diff --git a/package-lock.json b/package-lock.json index aba533c..bef892e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "discord-ollama", - "version": "0.8.7", + "version": "0.9.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "discord-ollama", - "version": "0.8.7", + "version": "0.9.0", "license": "ISC", "dependencies": { "discord.js": "^14.20.0", diff --git a/package.json b/package.json index 2165e30..30e75c5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-ollama", - "version": "0.8.7", + "version": "0.9.0", "description": "Ollama Integration into discord", "main": "build/index.js", "exports": "./build/index.js", diff --git a/src/commands/index.ts b/src/commands/index.ts index 56d23b4..e7d5035 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -9,6 +9,7 @@ import { ClearUserChannelHistory } from './cleanUserChannelHistory.js' import { PullModel } from './pullModel.js' import { SwitchModel } from './switchModel.js' import { DeleteModel } from './deleteModel.js' +import { Summary } from './summary.js' export default [ ThreadCreate, @@ -20,5 +21,6 @@ export default [ ClearUserChannelHistory, PullModel, SwitchModel, - DeleteModel + DeleteModel, + Summary ] as SlashCommand[] \ No newline at end of file diff --git a/src/commands/summary.ts b/src/commands/summary.ts new file mode 100644 index 0000000..b58be04 --- /dev/null +++ b/src/commands/summary.ts @@ -0,0 +1,50 @@ +import { Client, CommandInteraction, Message, MessageFlags, SendableChannels } from "discord.js"; +import { accessChannelContext, normalMessage, SlashCommand, summarizeContextHistory, UserCommand, UserMessage } from "../utils/index.js"; +import { ollama } from "../client.js" + +export const Summary: SlashCommand = { + name: 'summary', + description: 'provides a summary of the chat history.', + + // Generate Summary from additional context + run: async (client: Client, interaction: CommandInteraction) => { + // fetch channel + const channel = await client.channels.fetch(interaction.channelId) + if (!channel || !UserCommand.includes(channel.type)) return + + // Defer + await interaction.deferReply({ flags: MessageFlags.Ephemeral }) + + // create summary using context + let channelContext: UserMessage[] = await new Promise((resolve) => { + accessChannelContext(interaction.channelId, (additionalContext) => { + if (additionalContext?.messages) + resolve(additionalContext.messages) + else + resolve([]) + }) + }) + + if (channelContext.length === 0) { + interaction.reply({ + content: `There are no recent chat messages in this channel.`, + flags: MessageFlags.Ephemeral + }) + return + } + + // Push summarize prompt + channelContext.push({ + role: "user", + content: "Please Summarize everything from the prior messages, provide in bullet point fashion on what people have said prior. For example: \"Someone mentioned/talked about ...\"", + images: [] + }) + + // todo: instead of a default of codellama, we can user default model somehow. Look into later. + const response: string = await summarizeContextHistory(ollama, "codellama", channelContext) + + console.log(response) + + interaction.editReply({ content: response }) + } +} \ No newline at end of file diff --git a/src/utils/handlers/chatHistoryHandler.ts b/src/utils/handlers/chatHistoryHandler.ts index 9bcf403..69cae19 100644 --- a/src/utils/handlers/chatHistoryHandler.ts +++ b/src/utils/handlers/chatHistoryHandler.ts @@ -89,6 +89,21 @@ export async function addToChannelContext(filename: string, channel : TextChanne } } +export async function accessChannelContext(filename: string, callback: (config: Channel | undefined) => void): Promise { + const fullFileName = `data/${filename}-context.json` + if (fs.existsSync(fullFileName)) { + fs.readFile(fullFileName, 'utf8', (error, data) => { + if (error) { + callback(undefined) + return // something went wrong... stop + } + callback(JSON.parse(data)) + }) + } else { + callback(undefined) // file not found + } +} + /** * Method to open the channel history * diff --git a/src/utils/messageNormal.ts b/src/utils/messageNormal.ts index 64d0d1e..ddee526 100644 --- a/src/utils/messageNormal.ts +++ b/src/utils/messageNormal.ts @@ -90,6 +90,38 @@ export async function normalMessage( return result } +export async function summarizeContextHistory( + ollama: Ollama, + model: string, + msgHist: UserMessage[], +): Promise { + // todo: this stuff to create a response in a simpler way! + try { + const params: ChatParams = { + model: model, + ollama: ollama, + msgHist: msgHist + } + const response: ChatResponse = await blockResponse(params) + let result = response.message.content + + if (hasThinking(result)) + result = result.replace(/[\s\S]*?<\/think>/g, '').trim() + + return result + } catch (error: any) { + console.log(`[Util: messageNormal] Error creating message: ${error.message}`) + if (error.message.includes('fetch failed')) + error.message = 'Missing ollama service on machine' + else if (error.message.includes('try pulling it first')) + error.message = `You do not have the ${model} downloaded. Ask an admin to pull it using the \`pull-model\` command.` + return `**Response generation failed.**\n\nReason: ${error.message}` + } +} + + +// Region: Helpers function hasThinking(message: string): boolean { return /[\s\S]*?<\/think>/i.test(message) } +// End Region: Helpers diff --git a/tests/commands.test.ts b/tests/commands.test.ts index 1596017..8950a6a 100644 --- a/tests/commands.test.ts +++ b/tests/commands.test.ts @@ -27,7 +27,7 @@ describe('Commands Existence', () => { // test specific commands in the object it('references specific commands', () => { const commandsString = commands.map(e => e.name).join(', ') - const expectedCommands = ['thread', 'private-thread', 'message-stream', 'toggle-chat', 'shutoff', 'modify-capacity', 'clear-user-channel-history', 'pull-model', 'switch-model', 'delete-model'] + const expectedCommands = ['thread', 'private-thread', 'message-stream', 'toggle-chat', 'shutoff', 'modify-capacity', 'clear-user-channel-history', 'pull-model', 'switch-model', 'delete-model', 'summary'] expect(commandsString).toBe(expectedCommands.join(', ')) }) })