From bd97432db313610ab6ec93f79773f9bb283d869d Mon Sep 17 00:00:00 2001 From: Stanislas Polu Date: Fri, 29 Nov 2024 16:57:01 +0100 Subject: [PATCH] connectors: slack bot use file API for attachments (#9015) * connectors: slack bot use file API for attachments * move to text/plain * front * simplify File * not supported folder * fix sdk/js headers * fix * useCaseMetadata when the conversation exists --- connectors/src/connectors/slack/bot.ts | 55 ++++++++++++++++++-------- front/lib/api/files/upload.ts | 7 ++++ front/lib/api/files/upsert.ts | 7 ++++ sdks/js/src/index.ts | 37 +++++++++-------- types/src/front/content_fragment.ts | 1 - types/src/front/files.ts | 1 + 6 files changed, 75 insertions(+), 33 deletions(-) diff --git a/connectors/src/connectors/slack/bot.ts b/connectors/src/connectors/slack/bot.ts index 54d0b23c3594..5df9c64bbf37 100644 --- a/connectors/src/connectors/slack/bot.ts +++ b/connectors/src/connectors/slack/bot.ts @@ -339,23 +339,10 @@ async function answerMessage( userType: slackUserInfo.is_bot ? "bot" : "user", }); - const buildContentFragmentRes = await makeContentFragment( - slackClient, - slackChannel, - slackThreadTs || slackMessageTs, - lastSlackChatBotMessage?.messageTs || slackThreadTs || slackMessageTs, - connector - ); - if (!DUST_FRONT_API) { throw new Error("DUST_FRONT_API environment variable is not defined"); } - if (slackUserInfo.is_bot) { - const botName = slackUserInfo.real_name; - requestedGroups = await slackConfig.getBotGroupIds(botName); - } - const userEmailHeader = slackChatBotMessage.slackEmail !== "unknown" ? slackChatBotMessage.slackEmail @@ -376,6 +363,21 @@ async function answerMessage( } ); + const buildContentFragmentRes = await makeContentFragment( + slackClient, + dustAPI, + slackChannel, + slackThreadTs || slackMessageTs, + lastSlackChatBotMessage?.messageTs || slackThreadTs || slackMessageTs, + connector, + lastSlackChatBotMessage?.conversationId || null + ); + + if (slackUserInfo.is_bot) { + const botName = slackUserInfo.real_name; + requestedGroups = await slackConfig.getBotGroupIds(botName); + } + const agentConfigurationsRes = await dustAPI.getAgentConfigurations(); if (agentConfigurationsRes.isErr()) { return new Err(new Error(agentConfigurationsRes.error.message)); @@ -690,10 +692,12 @@ export async function getBotEnabled( async function makeContentFragment( slackClient: WebClient, + dustAPI: DustAPI, channelId: string, threadTs: string, startingAtTs: string | null, - connector: ConnectorResource + connector: ConnectorResource, + conversationId: string | null ): Promise> { let allMessages: MessageElement[] = []; @@ -770,11 +774,30 @@ async function makeContentFragment( // Prepend $url to the content to make it available to the model. const section = `$url: ${url}\n${sectionFullText(content)}`; + const contentType = "dust-application/slack"; + const fileName = `slack_thread-${channel.channel.name}.txt`; + + const fileRes = await dustAPI.uploadFile({ + contentType, + fileName, + fileSize: section.length, + useCase: "conversation", + useCaseMetadata: conversationId + ? { + conversationId, + } + : undefined, + fileObject: new File([section], fileName, { type: contentType }), + }); + + if (fileRes.isErr()) { + return new Err(new Error(fileRes.error.message)); + } + return new Ok({ title: `Thread content from #${channel.channel.name}`, - content: section, url: url, - contentType: "dust-application/slack", + fileId: fileRes.value.id, context: null, }); } diff --git a/front/lib/api/files/upload.ts b/front/lib/api/files/upload.ts index 6a46c3cc2e1f..834e124fe293 100644 --- a/front/lib/api/files/upload.ts +++ b/front/lib/api/files/upload.ts @@ -354,6 +354,13 @@ const processingPerContentType: ProcessingPerContentType = { avatar: notSupportedError, tool_output: notSupportedError, }, + "dust-application/slack": { + conversation: storeRawText, + folder_document: notSupportedError, + folder_table: notSupportedError, + avatar: notSupportedError, + tool_output: notSupportedError, + }, }; const maybeApplyProcessing: ProcessingFunction = async ( diff --git a/front/lib/api/files/upsert.ts b/front/lib/api/files/upsert.ts index 4cbc0d96b1fa..ca4aaabeac5d 100644 --- a/front/lib/api/files/upsert.ts +++ b/front/lib/api/files/upsert.ts @@ -346,6 +346,13 @@ const processingPerContentType: ProcessingPerContentType = { avatar: notSupportedError, tool_output: notSupportedError, }, + "dust-application/slack": { + conversation: upsertDocumentToDatasource, + folder_document: notSupportedError, + folder_table: notSupportedError, + avatar: notSupportedError, + tool_output: notSupportedError, + }, }; const maybeApplyProcessing: ProcessingFunction = async ({ diff --git a/sdks/js/src/index.ts b/sdks/js/src/index.ts index 77c34a702ab4..0f7e3f097825 100644 --- a/sdks/js/src/index.ts +++ b/sdks/js/src/index.ts @@ -171,20 +171,8 @@ export class DustAPI { return new Ok(r.value.user); } - async request(args: RequestArgsType) { - // Conveniently clean path from any leading "/" just in case - args.path = args.path.replace(/^\/+/, ""); - - let url = `${this.apiUrl()}/api/v1/w/${ - args.overrideWorkspaceId ?? this.workspaceId() - }/${args.path}`; - - if (args.query) { - url += `?${args.query.toString()}`; - } - + async baseHeaders() { const headers: RequestInit["headers"] = { - "Content-Type": "application/json", Authorization: `Bearer ${await this.getApiKey()}`, }; if (this._credentials.groupIds) { @@ -196,6 +184,23 @@ export class DustAPI { if (this._credentials.extraHeaders) { Object.assign(headers, this._credentials.extraHeaders); } + return headers; + } + + async request(args: RequestArgsType) { + // Conveniently clean path from any leading "/" just in case + args.path = args.path.replace(/^\/+/, ""); + + let url = `${this.apiUrl()}/api/v1/w/${ + args.overrideWorkspaceId ?? this.workspaceId() + }/${args.path}`; + + if (args.query) { + url += `?${args.query.toString()}`; + } + + const headers = await this.baseHeaders(); + headers["Content-Type"] = "application/json"; const res = await this._fetchWithError(url, { method: args.method, @@ -801,6 +806,7 @@ export class DustAPI { fileName, fileSize, useCase, + useCaseMetadata, fileObject, }: FileUploadUrlRequestType & { fileObject: File }) { FileUploadUrlRequestSchema; @@ -812,6 +818,7 @@ export class DustAPI { fileName, fileSize, useCase, + useCaseMetadata, }, }); @@ -834,9 +841,7 @@ export class DustAPI { try { uploadResult = await fetch(file.uploadUrl, { method: "POST", - headers: { - Authorization: `Bearer ${await this.getApiKey()}`, - }, + headers: await this.baseHeaders(), body: formData, }); } catch (err) { diff --git a/types/src/front/content_fragment.ts b/types/src/front/content_fragment.ts index ac431abca9c4..5f1fbf78a183 100644 --- a/types/src/front/content_fragment.ts +++ b/types/src/front/content_fragment.ts @@ -20,7 +20,6 @@ export type ContentFragmentContextType = { export const supportedContentFragmentType = [ ...supportedUploadableContentType, - "dust-application/slack", ] as const; export type SupportedContentFragmentType = diff --git a/types/src/front/files.ts b/types/src/front/files.ts index d4603a8260d3..032498346434 100644 --- a/types/src/front/files.ts +++ b/types/src/front/files.ts @@ -85,6 +85,7 @@ const supportedPlainText = { "application/pdf": [".pdf"], "text/markdown": [".md", ".markdown"], "text/plain": [".txt"], + "dust-application/slack": [".txt"], } as const; // Supported content types for images.