From 26a974a239059009cffc8978794bfc359b8b5eec Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Thu, 8 Aug 2024 18:47:52 -0300 Subject: [PATCH] feat: function for openai assistant added --- CHANGELOG.md | 1 + .../migration.sql | 7 ++ prisma/postgresql-schema.prisma | 1 + src/api/integrations/openai/dto/openai.dto.ts | 1 + .../openai/services/openai.service.ts | 71 +++++++++++++++++-- .../openai/validate/openai.schema.ts | 1 + .../channels/whatsapp.baileys.service.ts | 5 ++ 7 files changed, 80 insertions(+), 7 deletions(-) create mode 100644 prisma/migrations/20240808210239_add_column_function_url_openaibot_table/migration.sql diff --git a/CHANGELOG.md b/CHANGELOG.md index e1103cc9..ef64a35c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Variables passed to the input in dify * OwnerJid passed to typebot +* Function for openai assistant added # 2.0.7-rc (2024-08-03 14:04) diff --git a/prisma/migrations/20240808210239_add_column_function_url_openaibot_table/migration.sql b/prisma/migrations/20240808210239_add_column_function_url_openaibot_table/migration.sql new file mode 100644 index 00000000..16ca6b5d --- /dev/null +++ b/prisma/migrations/20240808210239_add_column_function_url_openaibot_table/migration.sql @@ -0,0 +1,7 @@ +-- AlterTable +ALTER TABLE "Instance" ADD COLUMN "disconnectionAt" TIMESTAMP, +ADD COLUMN "disconnectionObject" JSONB, +ADD COLUMN "disconnectionReasonCode" INTEGER; + +-- AlterTable +ALTER TABLE "OpenaiBot" ADD COLUMN "functionUrl" VARCHAR(500); diff --git a/prisma/postgresql-schema.prisma b/prisma/postgresql-schema.prisma index ff29eded..c245ed9d 100644 --- a/prisma/postgresql-schema.prisma +++ b/prisma/postgresql-schema.prisma @@ -374,6 +374,7 @@ model OpenaiBot { description String? @db.VarChar(255) botType OpenaiBotType assistantId String? @db.VarChar(255) + functionUrl String? @db.VarChar(500) model String? @db.VarChar(100) systemMessages Json? @db.JsonB assistantMessages Json? @db.JsonB diff --git a/src/api/integrations/openai/dto/openai.dto.ts b/src/api/integrations/openai/dto/openai.dto.ts index ff562f5a..9f0b55d3 100644 --- a/src/api/integrations/openai/dto/openai.dto.ts +++ b/src/api/integrations/openai/dto/openai.dto.ts @@ -19,6 +19,7 @@ export class OpenaiDto { openaiCredsId: string; botType?: string; assistantId?: string; + functionUrl?: string; model?: string; systemMessages?: string[]; assistantMessages?: string[]; diff --git a/src/api/integrations/openai/services/openai.service.ts b/src/api/integrations/openai/services/openai.service.ts index 5fec9926..3c5bc620 100644 --- a/src/api/integrations/openai/services/openai.service.ts +++ b/src/api/integrations/openai/services/openai.service.ts @@ -242,6 +242,7 @@ export class OpenaiService { openaiCredsId: data.openaiCredsId, botType: data.botType, assistantId: data.assistantId, + functionUrl: data.functionUrl, model: data.model, systemMessages: data.systemMessages, assistantMessages: data.assistantMessages, @@ -407,6 +408,7 @@ export class OpenaiService { openaiCredsId: data.openaiCredsId, botType: data.botType, assistantId: data.assistantId, + functionUrl: data.functionUrl, model: data.model, systemMessages: data.systemMessages, assistantMessages: data.assistantMessages, @@ -1315,7 +1317,7 @@ export class OpenaiService { await instance.client.sendPresenceUpdate('composing', remoteJid); - const response = await this.getAIResponse(data.session.sessionId, runAssistant.id); + const response = await this.getAIResponse(data.session.sessionId, runAssistant.id, openaiBot.functionUrl); await instance.client.sendPresenceUpdate('paused', remoteJid); @@ -1345,18 +1347,73 @@ export class OpenaiService { return; } - private async getAIResponse(threadId: string, runId: string) { + private isJSON(str: string): boolean { + try { + JSON.parse(str); + return true; + } catch (e) { + return false; + } + } + + private async getAIResponse(threadId: string, runId: string, functionUrl: string) { const getRun = await this.client.beta.threads.runs.retrieve(threadId, runId); + let toolCalls; switch (getRun.status) { + case 'requires_action': + toolCalls = getRun?.required_action?.submit_tool_outputs?.tool_calls; + + if (toolCalls) { + for (const toolCall of toolCalls) { + const id = toolCall.id; + const functionName = toolCall?.function?.name; + const functionArgument = this.isJSON(toolCall?.function?.arguments) + ? JSON.parse(toolCall?.function?.arguments) + : toolCall?.function?.arguments; + + let output = null; + + try { + const { data } = await axios.post(functionUrl, { + name: functionName, + arguments: functionArgument, + }); + + output = JSON.stringify(data) + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); + } catch (error) { + output = JSON.stringify(error) + .replace(/\\/g, '\\\\') + .replace(/"/g, '\\"') + .replace(/\n/g, '\\n') + .replace(/\r/g, '\\r') + .replace(/\t/g, '\\t'); + } + + await this.client.beta.threads.runs.submitToolOutputs(threadId, runId, { + tool_outputs: [ + { + tool_call_id: id, + output, + }, + ], + }); + } + } + + return null; + case 'queued': await new Promise((resolve) => setTimeout(resolve, 1000)); - return this.getAIResponse(threadId, runId); + return this.getAIResponse(threadId, runId, functionUrl); case 'in_progress': await new Promise((resolve) => setTimeout(resolve, 1000)); - return this.getAIResponse(threadId, runId); - case 'requires_action': - return null; + return this.getAIResponse(threadId, runId, functionUrl); case 'completed': return await this.client.beta.threads.messages.list(threadId, { run_id: runId, @@ -1489,7 +1546,7 @@ export class OpenaiService { await instance.client.sendPresenceUpdate('composing', remoteJid); - const response = await this.getAIResponse(threadId, runAssistant.id); + const response = await this.getAIResponse(threadId, runAssistant.id, openaiBot.functionUrl); await instance.client.sendPresenceUpdate('paused', remoteJid); diff --git a/src/api/integrations/openai/validate/openai.schema.ts b/src/api/integrations/openai/validate/openai.schema.ts index d7b63315..4d782582 100644 --- a/src/api/integrations/openai/validate/openai.schema.ts +++ b/src/api/integrations/openai/validate/openai.schema.ts @@ -29,6 +29,7 @@ export const openaiSchema: JSONSchema7 = { openaiCredsId: { type: 'string' }, botType: { type: 'string', enum: ['assistant', 'chatCompletion'] }, assistantId: { type: 'string' }, + functionUrl: { type: 'string' }, model: { type: 'string' }, systemMessages: { type: 'array', items: { type: 'string' } }, assistantMessages: { type: 'array', items: { type: 'string' } }, diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 2bcfd654..c6e30545 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -3345,6 +3345,11 @@ export class BaileysStartupService extends ChannelStartupService { try { const group = await this.client.groupMetadata(id.groupJid); + if (!group) { + this.logger.error('Group not found'); + return null; + } + const picture = await this.profilePicture(group.id); return {