From 0817fbaebbeae60b8c8d1affb6634f0072dbba89 Mon Sep 17 00:00:00 2001 From: Abdullah bin Amir <38984510+abdullahbaa5@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:52:29 +0400 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20Dify.AI=20block=20(#1183)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented [Dify.AI](https://dify.ai) Block - Dify Features: -- Can Create Multiple Chat Bots -- Assign Knowledge base/vector database to chat bots -- Variables send by client to be used in the prompt -- Options of custom and cloud AI LLMs to be changed with on click - Dify API Function/Action Implemented -- Create Chat Message (Takes in input variables, query, conversation id, user id and returns answer, usage metadata and conversation id) - Future Implantations with this block -- Streaming response -- File Upload for GPT Vision -- Speech to text action ## Summary by CodeRabbit - **New Features** - Introduced the ability to create chat messages within the Dify platform. - Added secure authentication for Dify.AI accounts. - Implemented a new Dify.AI block with integrated chat message creation and logo display. - Enabled Dify.AI block in the repository for user access. - **Enhancements** - Enhanced security for API key input by changing it to a password field. - **Documentation** - Included new types to support Dify AI service responses. - **Refactor** - Updated schema imports and array listings to include the new Dify.AI block. --------- Co-authored-by: Baptiste Arnaud --- apps/docs/openapi/builder.json | 1 + apps/docs/openapi/viewer.json | 1 + .../difyAi/actions/createChatMessage.ts | 80 +++++++++++++++++++ packages/forge/blocks/difyAi/auth.ts | 23 ++++++ packages/forge/blocks/difyAi/constants.ts | 1 + packages/forge/blocks/difyAi/index.ts | 13 +++ packages/forge/blocks/difyAi/logo.tsx | 11 +++ packages/forge/blocks/difyAi/package.json | 16 ++++ packages/forge/blocks/difyAi/tsconfig.json | 10 +++ packages/forge/blocks/difyAi/types.ts | 9 +++ packages/forge/core/index.ts | 12 +++ packages/forge/repository/index.ts | 1 + packages/forge/schemas/index.ts | 4 +- packages/forge/schemas/package.json | 1 + pnpm-lock.yaml | 24 ++++++ 15 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 packages/forge/blocks/difyAi/actions/createChatMessage.ts create mode 100644 packages/forge/blocks/difyAi/auth.ts create mode 100644 packages/forge/blocks/difyAi/constants.ts create mode 100644 packages/forge/blocks/difyAi/index.ts create mode 100644 packages/forge/blocks/difyAi/logo.tsx create mode 100644 packages/forge/blocks/difyAi/package.json create mode 100644 packages/forge/blocks/difyAi/tsconfig.json create mode 100644 packages/forge/blocks/difyAi/types.ts diff --git a/apps/docs/openapi/builder.json b/apps/docs/openapi/builder.json index 1734065cce6..4af5105d79e 100644 --- a/apps/docs/openapi/builder.json +++ b/apps/docs/openapi/builder.json @@ -26853,6 +26853,7 @@ "cal-com", "chat-node", "qr-code", + "dify-ai", "mistral" ] }, diff --git a/apps/docs/openapi/viewer.json b/apps/docs/openapi/viewer.json index 4e3cc1d8e64..4f6527179da 100644 --- a/apps/docs/openapi/viewer.json +++ b/apps/docs/openapi/viewer.json @@ -10354,6 +10354,7 @@ "cal-com", "chat-node", "qr-code", + "dify-ai", "mistral" ] }, diff --git a/packages/forge/blocks/difyAi/actions/createChatMessage.ts b/packages/forge/blocks/difyAi/actions/createChatMessage.ts new file mode 100644 index 00000000000..a38cb5376f7 --- /dev/null +++ b/packages/forge/blocks/difyAi/actions/createChatMessage.ts @@ -0,0 +1,80 @@ +import { createAction, option } from '@typebot.io/forge' +import { isDefined, isEmpty } from '@typebot.io/lib' +import { got } from 'got' +import { auth } from '../auth' +import { DifyResponse } from '../types' +import { defaultBaseUrl } from '../constants' + +export const createChatMessage = createAction({ + auth, + name: 'Create Chat Message', + options: option.object({ + query: option.string.layout({ + label: 'Query', + placeholder: 'User input/question content', + inputType: 'textarea', + isRequired: true, + }), + conversation_id: option.string.layout({ + label: 'Conversation ID', + moreInfoTooltip: + 'Used to remember the conversation with the user. If empty, a new conversation id is created.', + }), + user: option.string.layout({ + label: 'User', + moreInfoTooltip: + 'The user identifier, defined by the developer, must ensure uniqueness within the app.', + }), + inputs: option.keyValueList.layout({ + accordion: 'Inputs', + }), + responseMapping: option + .saveResponseArray(['Answer', 'Conversation ID', 'Total Tokens']) + .layout({ + accordion: 'Save response', + }), + }), + getSetVariableIds: ({ responseMapping }) => + responseMapping?.map((r) => r.variableId).filter(isDefined) ?? [], + run: { + server: async ({ + credentials: { apiEndpoint, apiKey }, + options: { conversation_id, query, user, inputs, responseMapping }, + variables, + }) => { + const res: DifyResponse = await got + .post((apiEndpoint ?? defaultBaseUrl) + '/v1/chat-messages', { + headers: { + Authorization: `Bearer ${apiKey}`, + }, + json: { + inputs: inputs?.reduce((acc, { key, value }) => { + if (isEmpty(key) || isEmpty(value)) return acc + return { + ...acc, + [key]: value, + } + }, {}), + query, + response_mode: 'blocking', + conversation_id, + user, + }, + }) + .json() + + responseMapping?.forEach((mapping) => { + if (!mapping.variableId) return + + const item = mapping.item ?? 'Answer' + if (item === 'Answer') variables.set(mapping.variableId, res.answer) + + if (item === 'Conversation ID') + variables.set(mapping.variableId, res.conversation_id) + + if (item === 'Total Tokens') + variables.set(mapping.variableId, res.metadata.usage.total_tokens) + }) + }, + }, +}) diff --git a/packages/forge/blocks/difyAi/auth.ts b/packages/forge/blocks/difyAi/auth.ts new file mode 100644 index 00000000000..af3585c2970 --- /dev/null +++ b/packages/forge/blocks/difyAi/auth.ts @@ -0,0 +1,23 @@ +import { option, AuthDefinition } from '@typebot.io/forge' +import { defaultBaseUrl } from './constants' + +export const auth = { + type: 'encryptedCredentials', + name: 'Dify.AI account', + schema: option.object({ + apiEndpoint: option.string.layout({ + label: 'API Endpoint', + isRequired: true, + helperText: 'URI where the Service API is hosted.', + withVariableButton: false, + defaultValue: defaultBaseUrl, + }), + apiKey: option.string.layout({ + label: 'App API key', + isRequired: true, + helperText: 'API Secret Key for your Dify App.', + inputType: 'password', + withVariableButton: false, + }), + }), +} satisfies AuthDefinition diff --git a/packages/forge/blocks/difyAi/constants.ts b/packages/forge/blocks/difyAi/constants.ts new file mode 100644 index 00000000000..6afb17f15d2 --- /dev/null +++ b/packages/forge/blocks/difyAi/constants.ts @@ -0,0 +1 @@ +export const defaultBaseUrl = 'https://api.dify.ai' diff --git a/packages/forge/blocks/difyAi/index.ts b/packages/forge/blocks/difyAi/index.ts new file mode 100644 index 00000000000..66b87c67346 --- /dev/null +++ b/packages/forge/blocks/difyAi/index.ts @@ -0,0 +1,13 @@ +import { createBlock } from '@typebot.io/forge' +import { DifyAiLogo } from './logo' +import { auth } from './auth' +import { createChatMessage } from './actions/createChatMessage' + +export const difyAi = createBlock({ + id: 'dify-ai', + name: 'Dify.AI', + tags: ['dify', 'ai', 'documents', 'files', 'knowledge base'], + LightLogo: DifyAiLogo, + auth, + actions: [createChatMessage], +}) diff --git a/packages/forge/blocks/difyAi/logo.tsx b/packages/forge/blocks/difyAi/logo.tsx new file mode 100644 index 00000000000..ab6f9fa84f4 --- /dev/null +++ b/packages/forge/blocks/difyAi/logo.tsx @@ -0,0 +1,11 @@ +import React from 'react' + +export const DifyAiLogo = (props: React.SVGProps) => ( + + + +) diff --git a/packages/forge/blocks/difyAi/package.json b/packages/forge/blocks/difyAi/package.json new file mode 100644 index 00000000000..dcebf57c87c --- /dev/null +++ b/packages/forge/blocks/difyAi/package.json @@ -0,0 +1,16 @@ +{ + "name": "@typebot.io/dify-ai-block", + "version": "1.0.0", + "description": "", + "main": "index.ts", + "keywords": [], + "license": "ISC", + "devDependencies": { + "@typebot.io/forge": "workspace:*", + "@typebot.io/lib": "workspace:*", + "@typebot.io/tsconfig": "workspace:*", + "@types/react": "18.2.15", + "got": "12.6.0", + "typescript": "5.3.2" + } +} diff --git a/packages/forge/blocks/difyAi/tsconfig.json b/packages/forge/blocks/difyAi/tsconfig.json new file mode 100644 index 00000000000..1eb9c771729 --- /dev/null +++ b/packages/forge/blocks/difyAi/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@typebot.io/tsconfig/base.json", + "include": ["**/*.ts", "**/*.tsx"], + "exclude": ["node_modules"], + "compilerOptions": { + "lib": ["ESNext", "DOM"], + "noEmit": true, + "jsx": "react" + } +} diff --git a/packages/forge/blocks/difyAi/types.ts b/packages/forge/blocks/difyAi/types.ts new file mode 100644 index 00000000000..b9c381e05d7 --- /dev/null +++ b/packages/forge/blocks/difyAi/types.ts @@ -0,0 +1,9 @@ +export type DifyResponse = { + answer: string + metadata: { + usage: { + total_tokens: number + } + } + conversation_id: string +} diff --git a/packages/forge/core/index.ts b/packages/forge/core/index.ts index 8ace97a62f7..0b842229ae7 100644 --- a/packages/forge/core/index.ts +++ b/packages/forge/core/index.ts @@ -101,6 +101,18 @@ export const option = { z.enum(values).optional(), number: z.number().or(variableStringSchema).optional(), array: (schema: T) => z.array(schema).optional(), + keyValueList: z + .array( + z.object({ + key: z.string().optional().layout({ + label: 'Key', + }), + value: z.string().optional().layout({ + label: 'Value', + }), + }) + ) + .optional(), discriminatedUnion: < T extends string, J extends [ diff --git a/packages/forge/repository/index.ts b/packages/forge/repository/index.ts index 01c1e612da0..3fea1c78216 100644 --- a/packages/forge/repository/index.ts +++ b/packages/forge/repository/index.ts @@ -5,5 +5,6 @@ export const enabledBlocks = [ 'cal-com', 'chat-node', 'qr-code', + 'dify-ai', 'mistral', ] as const diff --git a/packages/forge/schemas/index.ts b/packages/forge/schemas/index.ts index 840cbabb687..960e59887cc 100644 --- a/packages/forge/schemas/index.ts +++ b/packages/forge/schemas/index.ts @@ -1,4 +1,5 @@ // Do not edit this file manually +import { difyAi } from '@typebot.io/dify-ai-block' import { mistral } from '@typebot.io/mistral-block' import { qrCode } from '@typebot.io/qrcode-block' import { chatNode } from '@typebot.io/chat-node-block' @@ -19,7 +20,8 @@ export const forgedBlocks = [ calCom, chatNode, qrCode, - mistral, + difyAi, + mistral ] as BlockDefinition<(typeof enabledBlocks)[number], any, any>[] export type ForgedBlockDefinition = (typeof forgedBlocks)[number] diff --git a/packages/forge/schemas/package.json b/packages/forge/schemas/package.json index 677099654ff..06f6df62a7f 100644 --- a/packages/forge/schemas/package.json +++ b/packages/forge/schemas/package.json @@ -14,6 +14,7 @@ "@typebot.io/cal-com-block": "workspace:*", "@typebot.io/chat-node-block": "workspace:*", "@typebot.io/qrcode-block": "workspace:*", + "@typebot.io/dify-ai-block": "workspace:*", "@typebot.io/mistral-block": "workspace:*" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a32834c2896..86aaab010a6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1212,6 +1212,27 @@ importers: specifier: 5.3.2 version: 5.3.2 + packages/forge/blocks/difyAi: + devDependencies: + '@typebot.io/forge': + specifier: workspace:* + version: link:../../core + '@typebot.io/lib': + specifier: workspace:* + version: link:../../../lib + '@typebot.io/tsconfig': + specifier: workspace:* + version: link:../../../tsconfig + '@types/react': + specifier: 18.2.15 + version: 18.2.15 + got: + specifier: 12.6.0 + version: 12.6.0 + typescript: + specifier: 5.3.2 + version: 5.3.2 + packages/forge/blocks/mistral: dependencies: '@mistralai/mistralai': @@ -1352,6 +1373,9 @@ importers: '@typebot.io/chat-node-block': specifier: workspace:* version: link:../blocks/chatNode + '@typebot.io/dify-ai-block': + specifier: workspace:* + version: link:../blocks/difyAi '@typebot.io/forge': specifier: workspace:* version: link:../core