diff --git a/.changeset/bright-garlics-lick.md b/.changeset/bright-garlics-lick.md new file mode 100644 index 0000000..04b86d4 --- /dev/null +++ b/.changeset/bright-garlics-lick.md @@ -0,0 +1,6 @@ +--- +'slack-example': patch +'@callstack/byorg-slack': patch +--- + +Added useful plugins for normalizing threads (Slack started adding "New chat" message). Updated example slack app. diff --git a/examples/slack/src/index.ts b/examples/slack/src/index.ts index d9c8583..8a447dc 100644 --- a/examples/slack/src/index.ts +++ b/examples/slack/src/index.ts @@ -1,7 +1,7 @@ import { VercelChatModelAdapter, createApp } from '@callstack/byorg-core'; import { createOpenAI } from '@ai-sdk/openai'; import { logger, requireEnv } from '@callstack/byorg-utils'; -import { createSlackApp } from '@callstack/byorg-slack'; +import { createSlackApp, slackThreadNormalizerPlugin } from '@callstack/byorg-slack'; const LANGUAGE_MODEL = 'gpt-4o-2024-11-20'; const API_KEY = requireEnv('OPENAI_API_KEY'); @@ -27,6 +27,8 @@ const SYSTEM_PROMPT = 'Your name is Byorg. You are a helpful AI Assistant.'; const app = createApp({ chatModel, systemPrompt: SYSTEM_PROMPT, + // Normalize messages coming from Slack AI apps + plugins: [slackThreadNormalizerPlugin], }); const slack = createSlackApp({ diff --git a/packages/slack/src/__tests__/messagaes.test.ts b/packages/slack/src/__tests__/messages.test.ts similarity index 100% rename from packages/slack/src/__tests__/messagaes.test.ts rename to packages/slack/src/__tests__/messages.test.ts diff --git a/packages/slack/src/__tests__/plugins.test.ts b/packages/slack/src/__tests__/plugins.test.ts new file mode 100644 index 0000000..028d0b3 --- /dev/null +++ b/packages/slack/src/__tests__/plugins.test.ts @@ -0,0 +1,24 @@ +import { createApp, createMockChatModel, Message } from '@callstack/byorg-core'; +import { describe, expect, it, vitest } from 'vitest'; +import { slackThreadNormalizerPlugin } from '../plugins/thread-normalization.js'; + +describe('threadNormalizer', () => { + it('should remove slack "New chat" initial message', async () => { + const messages: Message[] = [ + { role: 'user', content: 'New chat\n' }, + { role: 'user', content: 'Hello!' }, + ]; + const baseModel = createMockChatModel({ delay: 0, seed: 3 }); + + const modelSpy = vitest.spyOn(baseModel, 'generateResponse'); + + const app = createApp({ + chatModel: baseModel, + plugins: [slackThreadNormalizerPlugin], + }); + + await app.processMessages(messages); + + expect(modelSpy.mock.calls[0][0]['messages']).toEqual([{ role: 'user', content: 'Hello!' }]); + }); +}); diff --git a/packages/slack/src/index.ts b/packages/slack/src/index.ts index 3e31263..947ebf7 100644 --- a/packages/slack/src/index.ts +++ b/packages/slack/src/index.ts @@ -6,3 +6,4 @@ export { slackEntityResolverPlugin, extractUserMentionsFromMessage, } from './plugins/entity-resolver.js'; +export { slackThreadNormalizerPlugin } from './plugins/thread-normalization.js'; diff --git a/packages/slack/src/plugins/thread-normalization.ts b/packages/slack/src/plugins/thread-normalization.ts new file mode 100644 index 0000000..f0ad7cd --- /dev/null +++ b/packages/slack/src/plugins/thread-normalization.ts @@ -0,0 +1,20 @@ +import { ApplicationPlugin, MessageResponse } from '@callstack/byorg-core'; +import { logger } from '@callstack/byorg-utils'; + +/** + * Slack plugin for normalizing messages coming from Slack [AI apps](https://api.slack.com/docs/apps/ai). + */ +export const slackThreadNormalizerPlugin: ApplicationPlugin = { + name: 'slack-thread-normalizer', + middleware: async (context, next): Promise => { + const { messages } = context; + + if (messages[0].content === 'New chat\n' && messages[1]?.role === 'user') { + context.messages = messages.slice(1); + logger.debug('Removed Slack "New Chat" message'); + } + + // Continue middleware chain + return await next(); + }, +};