From 258832435902e9be682cf898372acb2174a70615 Mon Sep 17 00:00:00 2001 From: Aric Lasry Date: Thu, 5 Oct 2023 16:14:30 +0200 Subject: [PATCH] Add support for Slack mentions --- connectors/src/connectors/slack/bot.ts | 54 ++++++++++++++++++++++++-- connectors/src/lib/dust_api.ts | 25 ++++++++++++ connectors/src/lib/edit_distance.ts | 41 +++++++++++++++++++ 3 files changed, 117 insertions(+), 3 deletions(-) create mode 100644 connectors/src/lib/edit_distance.ts diff --git a/connectors/src/connectors/slack/bot.ts b/connectors/src/connectors/slack/bot.ts index 2cd5f8a04f88..7901bc002e14 100644 --- a/connectors/src/connectors/slack/bot.ts +++ b/connectors/src/connectors/slack/bot.ts @@ -5,6 +5,7 @@ import { DustAPI, RetrievalDocumentType, } from "@connectors/lib/dust_api"; +import { editDistance } from "@connectors/lib/edit_distance"; import { Connector, ModelId, @@ -189,13 +190,59 @@ async function botAnswerMessage( } } } + // Extract all ~mentions. + const mentionCandidates = message.match(/~(\S+)/g) || []; + + let mentions: { assistantName: string; assistantId: string }[] = []; + if (mentionCandidates.length > 0) { + const agentConfigurationsRes = await dustAPI.getAgentConfigurations(); + if (agentConfigurationsRes.isErr()) { + return new Err(new Error(agentConfigurationsRes.error.message)); + } + const agentConfigurations = agentConfigurationsRes.value; + for (const mc of mentionCandidates) { + const scores: { + assistantId: string; + assistantName: string; + distance: number; + }[] = []; + for (const agentConfiguration of agentConfigurations) { + const distance = editDistance( + mc.slice(1).toLocaleLowerCase(), + agentConfiguration.name.toLowerCase() + ); + scores.push({ + assistantId: agentConfiguration.sId, + assistantName: agentConfiguration.name, + distance: distance, + }); + } + scores.sort((a, b) => { + return a.distance - b.distance; + }); + const bestScore = scores[0]; + if (bestScore) { + mentions.push({ + assistantId: bestScore.assistantId, + assistantName: bestScore.assistantName, + }); + } + } + } + if (mentions.length === 0) { + mentions.push({ assistantId: "dust", assistantName: "dust" }); + } + // for now we support only one mention. + mentions = mentions.slice(0, 1); const convRes = await dustAPI.createConversation({ title: null, visibility: "unlisted", message: { content: message, - mentions: [{ configurationId: "dust" }], + mentions: mentions.map((m) => { + return { configurationId: m.assistantId }; + }), context: { timezone: slackChatBotMessage.slackTimezone || "Europe/Paris", username: slackChatBotMessage.slackUserName, @@ -239,7 +286,8 @@ async function botAnswerMessage( return new Err(new Error(streamRes.error.message)); } - let fullAnswer = ""; + const botIdentity = `@${mentions[0]?.assistantName}:\n`; + let fullAnswer = botIdentity; let action: AgentActionType | null = null; let lastSentDate = new Date(); for await (const event of streamRes.value.eventStream) { @@ -282,7 +330,7 @@ async function botAnswerMessage( break; } case "agent_generation_success": { - fullAnswer = event.text; + fullAnswer = `${botIdentity}${event.text}`; let finalAnswer = _processCiteMention(fullAnswer, action); finalAnswer += `\n\n <${DUST_API}/w/${connector.workspaceId}/assistant/${conversation.sId}|Continue this conversation on Dust>`; diff --git a/connectors/src/lib/dust_api.ts b/connectors/src/lib/dust_api.ts index a49261c979fc..674b242a2dfa 100644 --- a/connectors/src/lib/dust_api.ts +++ b/connectors/src/lib/dust_api.ts @@ -304,6 +304,11 @@ export type ConversationType = { content: (UserMessageType[] | AgentMessageType[])[]; }; +type AgentConfigurationType = { + sId: string; + name: string; +}; + export class DustAPI { _credentials: DustAPICredentials; @@ -504,4 +509,24 @@ export class DustAPI { } return new Ok(json.conversation as ConversationType); } + + async getAgentConfigurations() { + const res = await fetch( + `${DUST_API}/api/v1/w/${this.workspaceId()}/assistant/agent_configurations`, + { + method: "GET", + headers: { + Authorization: `Bearer ${this._credentials.apiKey}`, + "Content-Type": "application/json", + }, + } + ); + + const json = await res.json(); + + if (json.error) { + return new Err(json.error as DustAPIErrorResponse); + } + return new Ok(json.agentConfigurations as AgentConfigurationType[]); + } } diff --git a/connectors/src/lib/edit_distance.ts b/connectors/src/lib/edit_distance.ts new file mode 100644 index 000000000000..ea26a4c554f1 --- /dev/null +++ b/connectors/src/lib/edit_distance.ts @@ -0,0 +1,41 @@ +// Returns the Levenshtein distance between str1 and str2. +export function editDistance(str1: string, str2: string): number { + const matrix: number[][] = Array.from({ length: str1.length + 1 }, () => + Array(str2.length + 1).fill(0) + ); + + const len1 = str1.length; + const len2 = str2.length; + + for (let i = 0; i <= len1; i++) { + // @ts-expect-error array of array apparently don't play well with TS + matrix[i][0] = i; + } + + for (let j = 0; j <= len2; j++) { + // @ts-expect-error array of array apparently don't play well with TS + matrix[0][j] = j; + } + + for (let i = 1; i <= len1; i++) { + for (let j = 1; j <= len2; j++) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + // @ts-expect-error array of array apparently don't play well with TS + matrix[i][j] = matrix[i - 1][j - 1]; + } else { + // @ts-expect-error array of array apparently don't play well with TS + matrix[i][j] = Math.min( + // @ts-expect-error array of array apparently don't play well with TS + matrix[i - 1][j - 1] + 1, + // @ts-expect-error array of array apparently don't play well with TS + Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1) + ); + } + } + } + // @ts-expect-error array of array apparently don't play well with TS + return matrix[len1][len2]; +} + +// Test the function +console.log(editDistance("kitten", "sitting")); // Output: 3