From d376b8540e866a1d19db2d42a123d4138bf58528 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Thu, 25 Jul 2024 16:41:09 +0800 Subject: [PATCH 001/275] add file-uploader --- .../base/file-uploader/file-type-icon.tsx | 86 +++++++++++++++++++ .../file-in-attachment-item.tsx | 29 +++++++ .../file-uploader-in-attachment/index.tsx | 47 ++++++++++ .../file-uploader-in-chat-input/index.tsx | 0 .../components/base/file-uploader/index.ts | 2 + .../components/base/file-uploader/types.ts | 14 +++ 6 files changed, 178 insertions(+) create mode 100644 web/app/components/base/file-uploader/file-type-icon.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx create mode 100644 web/app/components/base/file-uploader/index.ts create mode 100644 web/app/components/base/file-uploader/types.ts diff --git a/web/app/components/base/file-uploader/file-type-icon.tsx b/web/app/components/base/file-uploader/file-type-icon.tsx new file mode 100644 index 0000000000..6ca32928aa --- /dev/null +++ b/web/app/components/base/file-uploader/file-type-icon.tsx @@ -0,0 +1,86 @@ +import { memo } from 'react' +import { + RiFile3Fill, + RiFileCodeFill, + RiFileExcelFill, + RiFileGifFill, + RiFileImageFill, + RiFileMusicFill, + RiFilePdf2Fill, + RiFilePpt2Fill, + RiFileTextFill, + RiFileVideoFill, + RiFileWordFill, + RiMarkdownFill, +} from '@remixicon/react' +import { FileTypeEnum } from './types' +import cn from '@/utils/classnames' + +const FILE_TYPE_ICON_MAP = { + [FileTypeEnum.PDF]: { + component: RiFilePdf2Fill, + color: 'text-[#EA3434]', + }, + [FileTypeEnum.IMAGE]: { + component: RiFileImageFill, + color: 'text-[#00B2EA]', + }, + [FileTypeEnum.VIDEO]: { + component: RiFileVideoFill, + color: 'text-[#844FDA]', + }, + [FileTypeEnum.AUDIO]: { + component: RiFileMusicFill, + color: 'text-[#FF3093]', + }, + [FileTypeEnum.DOCUMENT]: { + component: RiFileTextFill, + color: 'text-[#6F8BB5]', + }, + [FileTypeEnum.CODE]: { + component: RiFileCodeFill, + color: 'text-[#BCC0D1]', + }, + [FileTypeEnum.MARKDOWN]: { + component: RiMarkdownFill, + color: 'text-[#309BEC]', + }, + [FileTypeEnum.OTHER]: { + component: RiFile3Fill, + color: 'text-[#BCC0D1]', + }, + [FileTypeEnum.EXCEL]: { + component: RiFileExcelFill, + color: 'text-[#01AC49]', + }, + [FileTypeEnum.WORD]: { + component: RiFileWordFill, + color: 'text-[#2684FF]', + }, + [FileTypeEnum.PPT]: { + component: RiFilePpt2Fill, + color: 'text-[#FF650F]', + }, + [FileTypeEnum.GIF]: { + component: RiFileGifFill, + color: 'text-[#00B2EA]', + }, +} +type FileTypeIconProps = { + type: keyof typeof FileTypeEnum + className?: string +} +const FileTypeIcon = ({ + type, + className, +}: FileTypeIconProps) => { + const Icon = FILE_TYPE_ICON_MAP[type].component + const color = FILE_TYPE_ICON_MAP[type].color + + if (!Icon) + return null + + return +} + +export default memo(FileTypeIcon) diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx new file mode 100644 index 0000000000..ee3460039d --- /dev/null +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx @@ -0,0 +1,29 @@ +import { memo } from 'react' +import { + RiDeleteBinLine, +} from '@remixicon/react' +import FileTypeIcon from '../file-type-icon' +import ActionButton from '@/app/components/base/action-button' + +const FileInAttachmentItem = () => { + return ( +
+
+ +
+
+
Yellow mountain range.jpg
+
+ JPG + + 21.5 MB +
+
+ + + +
+ ) +} + +export default memo(FileInAttachmentItem) diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx new file mode 100644 index 0000000000..87841b3a18 --- /dev/null +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx @@ -0,0 +1,47 @@ +import { memo } from 'react' +import { + RiLink, + RiUploadCloud2Line, +} from '@remixicon/react' +import FileInAttachmentItem from './file-in-attachment-item' +import Button from '@/app/components/base/button' + +const FileUploaderInAttachment = () => { + const options = [ + { + value: 'local', + label: 'Local upload', + icon: , + }, + { + value: 'link', + label: 'Paste file link', + icon: , + }, + ] + + return ( +
+
+ { + options.map(option => ( + + )) + } +
+
+ + +
+
+ ) +} + +export default memo(FileUploaderInAttachment) diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/index.tsx new file mode 100644 index 0000000000..e69de29bb2 diff --git a/web/app/components/base/file-uploader/index.ts b/web/app/components/base/file-uploader/index.ts new file mode 100644 index 0000000000..c8111581c9 --- /dev/null +++ b/web/app/components/base/file-uploader/index.ts @@ -0,0 +1,2 @@ +export { default as FileUploaderInAttachment } from './file-uploader-in-attachment' +export { default as FileTypeIcon } from './file-type-icon' diff --git a/web/app/components/base/file-uploader/types.ts b/web/app/components/base/file-uploader/types.ts new file mode 100644 index 0000000000..8a17505eaa --- /dev/null +++ b/web/app/components/base/file-uploader/types.ts @@ -0,0 +1,14 @@ +export enum FileTypeEnum { + IMAGE = 'IMAGE', + VIDEO = 'VIDEO', + AUDIO = 'AUDIO', + DOCUMENT = 'DOCUMENT', + CODE = 'CODE', + PDF = 'PDF', + MARKDOWN = 'MARKDOWN', + EXCEL = 'EXCEL', + WORD = 'WORD', + PPT = 'PPT', + GIF = 'GIF', + OTHER = 'OTHER', +} From 6fafd410d264988684070459ff829503a0bc0632 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 26 Jul 2024 11:21:17 +0800 Subject: [PATCH 002/275] feat: doc extract struct --- .../workflow/block-selector/constants.tsx | 5 +++ web/app/components/workflow/constants.ts | 17 +++++++ .../nodes/_base/components/variable/utils.ts | 10 +++++ .../components/workflow/nodes/constants.ts | 4 ++ .../workflow/nodes/doc-extractor/default.ts | 35 +++++++++++++++ .../workflow/nodes/doc-extractor/node.tsx | 40 +++++++++++++++++ .../workflow/nodes/doc-extractor/panel.tsx | 45 +++++++++++++++++++ .../workflow/nodes/doc-extractor/types.ts | 5 +++ .../nodes/doc-extractor/use-config.ts | 35 +++++++++++++++ .../components/node-variable-item.tsx | 4 +- web/app/components/workflow/types.ts | 3 ++ web/i18n/en-US/workflow.ts | 4 ++ 12 files changed, 206 insertions(+), 1 deletion(-) create mode 100644 web/app/components/workflow/nodes/doc-extractor/default.ts create mode 100644 web/app/components/workflow/nodes/doc-extractor/node.tsx create mode 100644 web/app/components/workflow/nodes/doc-extractor/panel.tsx create mode 100644 web/app/components/workflow/nodes/doc-extractor/types.ts create mode 100644 web/app/components/workflow/nodes/doc-extractor/use-config.ts diff --git a/web/app/components/workflow/block-selector/constants.tsx b/web/app/components/workflow/block-selector/constants.tsx index 517d9356f2..3b227a9853 100644 --- a/web/app/components/workflow/block-selector/constants.tsx +++ b/web/app/components/workflow/block-selector/constants.tsx @@ -69,6 +69,11 @@ export const BLOCKS: Block[] = [ type: BlockEnum.HttpRequest, title: 'HTTP Request', }, + { + classification: BlockClassificationEnum.Utilities, + type: BlockEnum.DocExtractor, + title: 'Doc Extractor', + }, ] export const BLOCK_CLASSIFICATIONS: string[] = [ diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index 46d12764f1..eb615a6651 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -14,6 +14,7 @@ import ToolDefault from './nodes/tool/default' import VariableAssignerDefault from './nodes/variable-assigner/default' import EndNodeDefault from './nodes/end/default' import IterationDefault from './nodes/iteration/default' +import DocExtractorDefault from './nodes/doc-extractor/default' type NodesExtraData = { author: string @@ -160,6 +161,16 @@ export const NODES_EXTRA_DATA: Record = { getAvailableNextNodes: ToolDefault.getAvailableNextNodes, checkValid: ToolDefault.checkValid, }, + [BlockEnum.DocExtractor]: { + author: 'Dify', + about: '', + availablePrevNodes: [], + availableNextNodes: [], + getAvailablePrevNodes: DocExtractorDefault.getAvailablePrevNodes, + getAvailableNextNodes: DocExtractorDefault.getAvailableNextNodes, + checkValid: DocExtractorDefault.checkValid, + }, + } export const ALL_CHAT_AVAILABLE_BLOCKS = Object.keys(NODES_EXTRA_DATA).filter(key => key !== BlockEnum.End && key !== BlockEnum.Start) as BlockEnum[] @@ -274,6 +285,12 @@ export const NODES_INITIAL_DATA = { desc: '', ...ToolDefault.defaultValue, }, + [BlockEnum.DocExtractor]: { + type: BlockEnum.DocExtractor, + title: '', + desc: '', + ...DocExtractorDefault.defaultValue, + }, } export const NODE_WIDTH = 240 diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 07c9a13dac..9e99792085 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -235,6 +235,16 @@ const formatItem = ( break } + case BlockEnum.DocExtractor: { + res.vars = [ + { + variable: 'text', + type: VarType.string, + }, + ] + break + } + case 'env': { res.vars = data.envList.map((env: EnvironmentVariable) => { return { diff --git a/web/app/components/workflow/nodes/constants.ts b/web/app/components/workflow/nodes/constants.ts index a97aa086ed..bed2d6069b 100644 --- a/web/app/components/workflow/nodes/constants.ts +++ b/web/app/components/workflow/nodes/constants.ts @@ -28,6 +28,8 @@ import ParameterExtractorNode from './parameter-extractor/node' import ParameterExtractorPanel from './parameter-extractor/panel' import IterationNode from './iteration/node' import IterationPanel from './iteration/panel' +import DocExtractorNode from './doc-extractor/node' +import DocExtractorPanel from './doc-extractor/panel' export const NodeComponentMap: Record> = { [BlockEnum.Start]: StartNode, @@ -45,6 +47,7 @@ export const NodeComponentMap: Record> = { [BlockEnum.VariableAggregator]: VariableAssignerNode, [BlockEnum.ParameterExtractor]: ParameterExtractorNode, [BlockEnum.Iteration]: IterationNode, + [BlockEnum.DocExtractor]: DocExtractorNode, } export const PanelComponentMap: Record> = { @@ -63,6 +66,7 @@ export const PanelComponentMap: Record> = { [BlockEnum.VariableAggregator]: VariableAssignerPanel, [BlockEnum.ParameterExtractor]: ParameterExtractorPanel, [BlockEnum.Iteration]: IterationPanel, + [BlockEnum.DocExtractor]: DocExtractorPanel, } export const CUSTOM_NODE_TYPE = 'custom' diff --git a/web/app/components/workflow/nodes/doc-extractor/default.ts b/web/app/components/workflow/nodes/doc-extractor/default.ts new file mode 100644 index 0000000000..204b1bcb5a --- /dev/null +++ b/web/app/components/workflow/nodes/doc-extractor/default.ts @@ -0,0 +1,35 @@ +import { BlockEnum } from '../../types' +import type { NodeDefault } from '../../types' +import { type DocExtractorNodeType } from './types' +import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants' +const i18nPrefix = 'workflow.errorMsg' + +const nodeDefault: NodeDefault = { + defaultValue: { + variable: [], + }, + getAvailablePrevNodes(isChatMode: boolean) { + const nodes = isChatMode + ? ALL_CHAT_AVAILABLE_BLOCKS + : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) + return nodes + }, + getAvailableNextNodes(isChatMode: boolean) { + const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS + return nodes + }, + checkValid(payload: DocExtractorNodeType, t: any) { + let errorMessages = '' + const { variable } = payload + + if (!errorMessages && !variable?.length) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') }) + + return { + isValid: !errorMessages, + errorMessage: errorMessages, + } + }, +} + +export default nodeDefault diff --git a/web/app/components/workflow/nodes/doc-extractor/node.tsx b/web/app/components/workflow/nodes/doc-extractor/node.tsx new file mode 100644 index 0000000000..8558ca81eb --- /dev/null +++ b/web/app/components/workflow/nodes/doc-extractor/node.tsx @@ -0,0 +1,40 @@ +import type { FC } from 'react' +import React from 'react' +import { useNodes } from 'reactflow' +import { useTranslation } from 'react-i18next' +import NodeVariableItem from '../variable-assigner/components/node-variable-item' +import { type DocExtractorNodeType } from './types' +import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' + +const i18nPrefix = 'workflow.nodes.assigner' + +const NodeComponent: FC> = ({ + data, +}) => { + const { t } = useTranslation() + + const nodes: Node[] = useNodes() + const { variable } = data + + if (!variable || variable.length === 0) + return null + + const isSystem = isSystemVar(variable) + const isEnv = isENV(variable) + const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) + const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.') + return ( +
+
{t(`${i18nPrefix}.assignedVariable`)}
+ +
+ ) +} + +export default React.memo(NodeComponent) diff --git a/web/app/components/workflow/nodes/doc-extractor/panel.tsx b/web/app/components/workflow/nodes/doc-extractor/panel.tsx new file mode 100644 index 0000000000..612220b811 --- /dev/null +++ b/web/app/components/workflow/nodes/doc-extractor/panel.tsx @@ -0,0 +1,45 @@ +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import useConfig from './use-config' +import type { DocExtractorNodeType } from './types' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { type NodePanelProps } from '@/app/components/workflow/types' + +const i18nPrefix = 'workflow.nodes.docExtractor' + +const Panel: FC> = ({ + id, + data, +}) => { + const { t } = useTranslation() + + const { + readOnly, + inputs, + handleVarChanges, + filterVar, + } = useConfig(id, data) + + return ( +
+
+ + + +
+
+ ) +} + +export default React.memo(Panel) diff --git a/web/app/components/workflow/nodes/doc-extractor/types.ts b/web/app/components/workflow/nodes/doc-extractor/types.ts new file mode 100644 index 0000000000..054e4b6cf2 --- /dev/null +++ b/web/app/components/workflow/nodes/doc-extractor/types.ts @@ -0,0 +1,5 @@ +import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' + +export type DocExtractorNodeType = CommonNodeType & { + variable: ValueSelector +} diff --git a/web/app/components/workflow/nodes/doc-extractor/use-config.ts b/web/app/components/workflow/nodes/doc-extractor/use-config.ts new file mode 100644 index 0000000000..45ad397003 --- /dev/null +++ b/web/app/components/workflow/nodes/doc-extractor/use-config.ts @@ -0,0 +1,35 @@ +import { useCallback } from 'react' +import produce from 'immer' +import type { ValueSelector, Var } from '../../types' +import { VarType } from '../../types' +import { type DocExtractorNodeType } from './types' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { + useNodesReadOnly, +} from '@/app/components/workflow/hooks' + +const useConfig = (id: string, payload: DocExtractorNodeType) => { + const { nodesReadOnly: readOnly } = useNodesReadOnly() + + const { inputs, setInputs } = useNodeCrud(id, payload) + + const handleVarChanges = useCallback((variable: ValueSelector | string) => { + const newInputs = produce(inputs, (draft) => { + draft.variable = variable as ValueSelector + }) + setInputs(newInputs) + }, [inputs, setInputs]) + + const filterVar = useCallback((varPayload: Var) => { + return varPayload.type !== VarType.file + }, []) + + return { + readOnly, + inputs, + filterVar, + handleVarChanges, + } +} + +export default useConfig diff --git a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx index 6b32ebaf02..62911e521c 100644 --- a/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx +++ b/web/app/components/workflow/nodes/variable-assigner/components/node-variable-item.tsx @@ -8,12 +8,14 @@ import type { Node } from '@/app/components/workflow/types' import { BlockEnum } from '@/app/components/workflow/types' type NodeVariableItemProps = { + className?: string isEnv: boolean node: Node varName: string showBorder?: boolean } const NodeVariableItem = ({ + className, isEnv, node, varName, @@ -22,7 +24,7 @@ const NodeVariableItem = ({ return (
{!isEnv && (
diff --git a/web/app/components/workflow/types.ts b/web/app/components/workflow/types.ts index 8b0e3113bc..e455687ce3 100644 --- a/web/app/components/workflow/types.ts +++ b/web/app/components/workflow/types.ts @@ -25,6 +25,8 @@ export enum BlockEnum { Tool = 'tool', ParameterExtractor = 'parameter-extractor', Iteration = 'iteration', + DocExtractor = 'doc-extractor', + ListFilter = 'list-filter', } export type Branch = { @@ -193,6 +195,7 @@ export enum VarType { secret = 'secret', boolean = 'boolean', object = 'object', + file = 'file', array = 'array', arrayString = 'array[string]', arrayNumber = 'array[number]', diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 233ba40450..c416ca296d 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -176,6 +176,7 @@ const translation = { 'iteration-start': 'Iteration Start', 'iteration': 'Iteration', 'parameter-extractor': 'Parameter Extractor', + 'doc-extractor': 'Doc Extractor', }, blocksAbout: { 'start': 'Define the initial parameters for launching a workflow', @@ -192,6 +193,7 @@ const translation = { 'variable-aggregator': 'Aggregate multi-branch variables into a single variable for unified configuration of downstream nodes.', 'iteration': 'Perform multiple steps on a list object until all results are outputted.', 'parameter-extractor': 'Use LLM to extract structured parameters from natural language for tool invocations or HTTP requests.', + 'doc-extractor': 'TODO', }, operator: { zoomIn: 'Zoom In', @@ -489,6 +491,8 @@ const translation = { showAuthor: 'Show Author', }, }, + docExtractor: { + }, }, tracing: { stopBy: 'Stop by {{user}}', From 394f06a27a2c4c219d0eb426bfb2c4b4adf4da88 Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 26 Jul 2024 11:55:09 +0800 Subject: [PATCH 003/275] feat: list filter --- .../workflow/block-selector/constants.tsx | 9 +++- web/app/components/workflow/constants.ts | 16 +++++++ .../nodes/_base/components/variable/utils.ts | 18 ++++++++ .../components/workflow/nodes/constants.ts | 4 ++ .../workflow/nodes/list-filter/default.ts | 35 +++++++++++++++ .../workflow/nodes/list-filter/node.tsx | 40 +++++++++++++++++ .../workflow/nodes/list-filter/panel.tsx | 45 +++++++++++++++++++ .../workflow/nodes/list-filter/types.ts | 5 +++ .../workflow/nodes/list-filter/use-config.ts | 35 +++++++++++++++ web/i18n/en-US/workflow.ts | 4 +- web/i18n/zh-Hans/workflow.ts | 4 ++ 11 files changed, 212 insertions(+), 3 deletions(-) create mode 100644 web/app/components/workflow/nodes/list-filter/default.ts create mode 100644 web/app/components/workflow/nodes/list-filter/node.tsx create mode 100644 web/app/components/workflow/nodes/list-filter/panel.tsx create mode 100644 web/app/components/workflow/nodes/list-filter/types.ts create mode 100644 web/app/components/workflow/nodes/list-filter/use-config.ts diff --git a/web/app/components/workflow/block-selector/constants.tsx b/web/app/components/workflow/block-selector/constants.tsx index 3b227a9853..de3efdd0eb 100644 --- a/web/app/components/workflow/block-selector/constants.tsx +++ b/web/app/components/workflow/block-selector/constants.tsx @@ -59,6 +59,11 @@ export const BLOCKS: Block[] = [ type: BlockEnum.VariableAggregator, title: 'Variable Aggregator', }, + { + classification: BlockClassificationEnum.Transform, + type: BlockEnum.DocExtractor, + title: 'Doc Extractor', + }, { classification: BlockClassificationEnum.Transform, type: BlockEnum.ParameterExtractor, @@ -71,8 +76,8 @@ export const BLOCKS: Block[] = [ }, { classification: BlockClassificationEnum.Utilities, - type: BlockEnum.DocExtractor, - title: 'Doc Extractor', + type: BlockEnum.ListFilter, + title: 'List Filter', }, ] diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts index eb615a6651..de72ce6401 100644 --- a/web/app/components/workflow/constants.ts +++ b/web/app/components/workflow/constants.ts @@ -15,6 +15,7 @@ import VariableAssignerDefault from './nodes/variable-assigner/default' import EndNodeDefault from './nodes/end/default' import IterationDefault from './nodes/iteration/default' import DocExtractorDefault from './nodes/doc-extractor/default' +import ListFilterDefault from './nodes/list-filter/default' type NodesExtraData = { author: string @@ -170,6 +171,15 @@ export const NODES_EXTRA_DATA: Record = { getAvailableNextNodes: DocExtractorDefault.getAvailableNextNodes, checkValid: DocExtractorDefault.checkValid, }, + [BlockEnum.ListFilter]: { + author: 'Dify', + about: '', + availablePrevNodes: [], + availableNextNodes: [], + getAvailablePrevNodes: ListFilterDefault.getAvailablePrevNodes, + getAvailableNextNodes: ListFilterDefault.getAvailableNextNodes, + checkValid: ListFilterDefault.checkValid, + }, } @@ -291,6 +301,12 @@ export const NODES_INITIAL_DATA = { desc: '', ...DocExtractorDefault.defaultValue, }, + [BlockEnum.ListFilter]: { + type: BlockEnum.ListFilter, + title: '', + desc: '', + ...ListFilterDefault.defaultValue, + }, } export const NODE_WIDTH = 240 diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 9e99792085..f1a93f9988 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -245,6 +245,24 @@ const formatItem = ( break } + case BlockEnum.ListFilter: { + res.vars = [ + { + variable: 'result', + type: VarType.array, // TODO dyn value + }, + { + variable: 'first_record', + type: VarType.string, // TODO dyn value + }, + { + variable: 'last_record', + type: VarType.string, // TODO dyn value + }, + ] + break + } + case 'env': { res.vars = data.envList.map((env: EnvironmentVariable) => { return { diff --git a/web/app/components/workflow/nodes/constants.ts b/web/app/components/workflow/nodes/constants.ts index bed2d6069b..b983110d39 100644 --- a/web/app/components/workflow/nodes/constants.ts +++ b/web/app/components/workflow/nodes/constants.ts @@ -30,6 +30,8 @@ import IterationNode from './iteration/node' import IterationPanel from './iteration/panel' import DocExtractorNode from './doc-extractor/node' import DocExtractorPanel from './doc-extractor/panel' +import ListFilterNode from './list-filter/node' +import ListFilterPanel from './list-filter/panel' export const NodeComponentMap: Record> = { [BlockEnum.Start]: StartNode, @@ -48,6 +50,7 @@ export const NodeComponentMap: Record> = { [BlockEnum.ParameterExtractor]: ParameterExtractorNode, [BlockEnum.Iteration]: IterationNode, [BlockEnum.DocExtractor]: DocExtractorNode, + [BlockEnum.ListFilter]: ListFilterNode, } export const PanelComponentMap: Record> = { @@ -67,6 +70,7 @@ export const PanelComponentMap: Record> = { [BlockEnum.ParameterExtractor]: ParameterExtractorPanel, [BlockEnum.Iteration]: IterationPanel, [BlockEnum.DocExtractor]: DocExtractorPanel, + [BlockEnum.ListFilter]: ListFilterPanel, } export const CUSTOM_NODE_TYPE = 'custom' diff --git a/web/app/components/workflow/nodes/list-filter/default.ts b/web/app/components/workflow/nodes/list-filter/default.ts new file mode 100644 index 0000000000..5fe4b44a8d --- /dev/null +++ b/web/app/components/workflow/nodes/list-filter/default.ts @@ -0,0 +1,35 @@ +import { BlockEnum } from '../../types' +import type { NodeDefault } from '../../types' +import { type ListFilterNodeType } from './types' +import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants' +const i18nPrefix = 'workflow.errorMsg' + +const nodeDefault: NodeDefault = { + defaultValue: { + variable: [], + }, + getAvailablePrevNodes(isChatMode: boolean) { + const nodes = isChatMode + ? ALL_CHAT_AVAILABLE_BLOCKS + : ALL_COMPLETION_AVAILABLE_BLOCKS.filter(type => type !== BlockEnum.End) + return nodes + }, + getAvailableNextNodes(isChatMode: boolean) { + const nodes = isChatMode ? ALL_CHAT_AVAILABLE_BLOCKS : ALL_COMPLETION_AVAILABLE_BLOCKS + return nodes + }, + checkValid(payload: ListFilterNodeType, t: any) { + let errorMessages = '' + const { variable } = payload + + if (!errorMessages && !variable?.length) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') }) + + return { + isValid: !errorMessages, + errorMessage: errorMessages, + } + }, +} + +export default nodeDefault diff --git a/web/app/components/workflow/nodes/list-filter/node.tsx b/web/app/components/workflow/nodes/list-filter/node.tsx new file mode 100644 index 0000000000..8d0b5f7aae --- /dev/null +++ b/web/app/components/workflow/nodes/list-filter/node.tsx @@ -0,0 +1,40 @@ +import type { FC } from 'react' +import React from 'react' +import { useNodes } from 'reactflow' +import { useTranslation } from 'react-i18next' +import NodeVariableItem from '../variable-assigner/components/node-variable-item' +import { type ListFilterNodeType } from './types' +import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' + +const i18nPrefix = 'workflow.nodes.assigner' + +const NodeComponent: FC> = ({ + data, +}) => { + const { t } = useTranslation() + + const nodes: Node[] = useNodes() + const { variable } = data + + if (!variable || variable.length === 0) + return null + + const isSystem = isSystemVar(variable) + const isEnv = isENV(variable) + const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) + const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.') + return ( +
+
{t(`${i18nPrefix}.assignedVariable`)}
+ +
+ ) +} + +export default React.memo(NodeComponent) diff --git a/web/app/components/workflow/nodes/list-filter/panel.tsx b/web/app/components/workflow/nodes/list-filter/panel.tsx new file mode 100644 index 0000000000..f1da053b9e --- /dev/null +++ b/web/app/components/workflow/nodes/list-filter/panel.tsx @@ -0,0 +1,45 @@ +import type { FC } from 'react' +import React from 'react' +import { useTranslation } from 'react-i18next' +import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import useConfig from './use-config' +import type { ListFilterNodeType } from './types' +import Field from '@/app/components/workflow/nodes/_base/components/field' +import { type NodePanelProps } from '@/app/components/workflow/types' + +const i18nPrefix = 'workflow.nodes.docExtractor' + +const Panel: FC> = ({ + id, + data, +}) => { + const { t } = useTranslation() + + const { + readOnly, + inputs, + handleVarChanges, + filterVar, + } = useConfig(id, data) + + return ( +
+
+ + + +
+
+ ) +} + +export default React.memo(Panel) diff --git a/web/app/components/workflow/nodes/list-filter/types.ts b/web/app/components/workflow/nodes/list-filter/types.ts new file mode 100644 index 0000000000..cf0f1cac9b --- /dev/null +++ b/web/app/components/workflow/nodes/list-filter/types.ts @@ -0,0 +1,5 @@ +import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' + +export type ListFilterNodeType = CommonNodeType & { + variable: ValueSelector +} diff --git a/web/app/components/workflow/nodes/list-filter/use-config.ts b/web/app/components/workflow/nodes/list-filter/use-config.ts new file mode 100644 index 0000000000..0dbf429057 --- /dev/null +++ b/web/app/components/workflow/nodes/list-filter/use-config.ts @@ -0,0 +1,35 @@ +import { useCallback } from 'react' +import produce from 'immer' +import type { ValueSelector, Var } from '../../types' +import { VarType } from '../../types' +import { type ListFilterNodeType } from './types' +import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' +import { + useNodesReadOnly, +} from '@/app/components/workflow/hooks' + +const useConfig = (id: string, payload: ListFilterNodeType) => { + const { nodesReadOnly: readOnly } = useNodesReadOnly() + + const { inputs, setInputs } = useNodeCrud(id, payload) + + const handleVarChanges = useCallback((variable: ValueSelector | string) => { + const newInputs = produce(inputs, (draft) => { + draft.variable = variable as ValueSelector + }) + setInputs(newInputs) + }, [inputs, setInputs]) + + const filterVar = useCallback((varPayload: Var) => { + return varPayload.type !== VarType.file + }, []) + + return { + readOnly, + inputs, + filterVar, + handleVarChanges, + } +} + +export default useConfig diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index c416ca296d..9b6f538380 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -177,6 +177,7 @@ const translation = { 'iteration': 'Iteration', 'parameter-extractor': 'Parameter Extractor', 'doc-extractor': 'Doc Extractor', + 'list-filter': 'List Filter', }, blocksAbout: { 'start': 'Define the initial parameters for launching a workflow', @@ -193,7 +194,8 @@ const translation = { 'variable-aggregator': 'Aggregate multi-branch variables into a single variable for unified configuration of downstream nodes.', 'iteration': 'Perform multiple steps on a list object until all results are outputted.', 'parameter-extractor': 'Use LLM to extract structured parameters from natural language for tool invocations or HTTP requests.', - 'doc-extractor': 'TODO', + 'doc-extractor': 'doc-extractor TODO', + 'list-filter': 'List Filter TODO', }, operator: { zoomIn: 'Zoom In', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 648c7c6891..5b1574ac6c 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -176,6 +176,8 @@ const translation = { 'iteration-start': '迭代开始', 'iteration': '迭代', 'parameter-extractor': '参数提取器', + 'doc-extractor': '文档提取器', + 'list-filter': '列表过滤器', }, blocksAbout: { 'start': '定义一个 workflow 流程启动的初始参数', @@ -192,6 +194,8 @@ const translation = { 'variable-aggregator': '将多路分支的变量聚合为一个变量,以实现下游节点统一配置。', 'iteration': '对列表对象执行多次步骤直至输出所有结果。', 'parameter-extractor': '利用 LLM 从自然语言内推理提取出结构化参数,用于后置的工具调用或 HTTP 请求。', + 'doc-extractor': 'doc-extractor TODO', + 'list-filter': 'List Filter TODO', }, operator: { zoomIn: '放大', From 09aa14ca82a3d350fdb46d648fdcb88e23435a7c Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 26 Jul 2024 14:11:03 +0800 Subject: [PATCH 004/275] feat: node icons --- .../assets/vender/workflow/docs-extractor.svg | 9 +++ .../assets/vender/workflow/list-filter.svg | 5 ++ .../src/vender/workflow/DocsExtractor.json | 64 +++++++++++++++++++ .../src/vender/workflow/DocsExtractor.tsx | 16 +++++ .../icons/src/vender/workflow/ListFilter.json | 38 +++++++++++ .../icons/src/vender/workflow/ListFilter.tsx | 16 +++++ .../base/icons/src/vender/workflow/index.ts | 2 + web/app/components/workflow/block-icon.tsx | 6 ++ 8 files changed, 156 insertions(+) create mode 100644 web/app/components/base/icons/assets/vender/workflow/docs-extractor.svg create mode 100644 web/app/components/base/icons/assets/vender/workflow/list-filter.svg create mode 100644 web/app/components/base/icons/src/vender/workflow/DocsExtractor.json create mode 100644 web/app/components/base/icons/src/vender/workflow/DocsExtractor.tsx create mode 100644 web/app/components/base/icons/src/vender/workflow/ListFilter.json create mode 100644 web/app/components/base/icons/src/vender/workflow/ListFilter.tsx diff --git a/web/app/components/base/icons/assets/vender/workflow/docs-extractor.svg b/web/app/components/base/icons/assets/vender/workflow/docs-extractor.svg new file mode 100644 index 0000000000..5b85003443 --- /dev/null +++ b/web/app/components/base/icons/assets/vender/workflow/docs-extractor.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/web/app/components/base/icons/assets/vender/workflow/list-filter.svg b/web/app/components/base/icons/assets/vender/workflow/list-filter.svg new file mode 100644 index 0000000000..8b91e4879c --- /dev/null +++ b/web/app/components/base/icons/assets/vender/workflow/list-filter.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/vender/workflow/DocsExtractor.json b/web/app/components/base/icons/src/vender/workflow/DocsExtractor.json new file mode 100644 index 0000000000..5b454590be --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/DocsExtractor.json @@ -0,0 +1,64 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "docs-extractor" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Vector" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "fill-rule": "evenodd", + "clip-rule": "evenodd", + "d": "M2.66663 3.33325C2.66663 2.22869 3.56206 1.33325 4.66663 1.33325H12.6666C13.0348 1.33325 13.3333 1.63173 13.3333 1.99992V13.9999C13.3333 14.3681 13.0348 14.6666 12.6666 14.6666H4.66663C3.56206 14.6666 2.66663 13.7712 2.66663 12.6666V3.33325ZM3.99996 10.7804V3.33325C3.99996 2.96507 4.29844 2.66659 4.66663 2.66659H12V10.6666H4.66663C4.43287 10.6666 4.20848 10.7067 3.99996 10.7804ZM12 11.9999H4.66663C4.29844 11.9999 3.99996 12.2984 3.99996 12.6666C3.99996 13.0348 4.29844 13.3333 4.66663 13.3333H12V11.9999Z", + "fill": "currentColor" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M8.12296 4.9385C8.18749 4.90624 8.23983 4.85394 8.27203 4.78942L8.70203 3.92954C8.82483 3.68385 9.17543 3.68385 9.29829 3.92954L9.72823 4.78942C9.76049 4.85394 9.81276 4.90624 9.87729 4.9385L10.7372 5.36844C10.9829 5.49128 10.9829 5.84189 10.7372 5.96473L9.87729 6.39467C9.81276 6.42692 9.76049 6.47923 9.72823 6.54375L9.29829 7.40365C9.17543 7.64932 8.82483 7.64932 8.70203 7.40365L8.27203 6.54375C8.23983 6.47923 8.18749 6.42692 8.12296 6.39467L7.26309 5.96473C7.01743 5.84189 7.01743 5.49128 7.26309 5.36844L8.12296 4.9385Z", + "fill": "currentColor" + }, + "children": [] + }, + { + "type": "element", + "name": "path", + "attributes": { + "d": "M5.71829 7.80752C5.757 7.78819 5.78838 7.75678 5.80773 7.71805L6.15459 7.02438C6.22829 6.87692 6.43865 6.87692 6.51236 7.02438L6.85923 7.71805C6.87856 7.75678 6.90996 7.78819 6.94863 7.80752L7.64236 8.15439C7.78976 8.22805 7.78976 8.43845 7.64236 8.51212L6.94863 8.85898C6.90996 8.87832 6.87856 8.90972 6.85923 8.94845L6.51236 9.64212C6.43865 9.78959 6.22829 9.78959 6.15459 9.64212L5.80773 8.94845C5.78838 8.90972 5.757 8.87832 5.71829 8.85898L5.02458 8.51212C4.87717 8.43845 4.87717 8.22805 5.02458 8.15439L5.71829 7.80752Z", + "fill": "currentColor" + }, + "children": [] + } + ] + } + ] + } + ] + }, + "name": "DocsExtractor" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/workflow/DocsExtractor.tsx b/web/app/components/base/icons/src/vender/workflow/DocsExtractor.tsx new file mode 100644 index 0000000000..355c68e582 --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/DocsExtractor.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './DocsExtractor.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'DocsExtractor' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/workflow/ListFilter.json b/web/app/components/base/icons/src/vender/workflow/ListFilter.json new file mode 100644 index 0000000000..568020f4a6 --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/ListFilter.json @@ -0,0 +1,38 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "16", + "height": "16", + "viewBox": "0 0 16 16", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "filter" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Vector", + "fill-rule": "evenodd", + "clip-rule": "evenodd", + "d": "M2 4C2 2.89543 2.89543 2 4 2L12 2C13.1046 2 14 2.89544 14 4V4.78105C14 5.31148 13.7893 5.82019 13.4142 6.19528L10.1953 9.4142C10.0702 9.53925 10 9.70881 10 9.8856V12.8713C10 13.427 9.65528 13.9246 9.13482 14.1198C9.13479 14.1198 9.13476 14.1198 9.13473 14.1198L7.80153 14.6197C6.92984 14.9467 6 14.3022 6 13.3713L6 9.8856C6 9.70883 5.92978 9.53926 5.80474 9.4142C5.80473 9.4142 5.80473 9.4142 5.80472 9.41419L2.58579 6.19526L3.05004 5.73102L2.58579 6.19526C2.21071 5.82019 2 5.31148 2 4.78105V4ZM4 3.33333C3.63181 3.33333 3.33333 3.63181 3.33333 4L3.33333 4.78105C3.33333 4.95786 3.40357 5.12743 3.5286 5.25246L6.74754 8.47139L6.74756 8.47141C7.12262 8.84649 7.33333 9.35518 7.33333 9.8856L7.33333 13.3713L8.66665 12.8713L8.66667 12.8713L8.66667 9.8856C8.66667 9.35518 8.87737 8.84648 9.25246 8.4714L12.4714 5.25246L12.4714 5.25244C12.5964 5.12742 12.6667 4.95787 12.6667 4.78105V4C12.6667 3.6318 12.3682 3.33333 12 3.33333L4 3.33333Z", + "fill": "currentColor" + }, + "children": [] + } + ] + } + ] + }, + "name": "ListFilter" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/workflow/ListFilter.tsx b/web/app/components/base/icons/src/vender/workflow/ListFilter.tsx new file mode 100644 index 0000000000..bf8eb27b49 --- /dev/null +++ b/web/app/components/base/icons/src/vender/workflow/ListFilter.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './ListFilter.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'ListFilter' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/workflow/index.ts b/web/app/components/base/icons/src/vender/workflow/index.ts index 94e20ae6a9..511703dcab 100644 --- a/web/app/components/base/icons/src/vender/workflow/index.ts +++ b/web/app/components/base/icons/src/vender/workflow/index.ts @@ -1,5 +1,6 @@ export { default as Answer } from './Answer' export { default as Code } from './Code' +export { default as DocsExtractor } from './DocsExtractor' export { default as End } from './End' export { default as Home } from './Home' export { default as Http } from './Http' @@ -8,6 +9,7 @@ export { default as IterationStart } from './IterationStart' export { default as Iteration } from './Iteration' export { default as Jinja } from './Jinja' export { default as KnowledgeRetrieval } from './KnowledgeRetrieval' +export { default as ListFilter } from './ListFilter' export { default as Llm } from './Llm' export { default as ParameterExtractor } from './ParameterExtractor' export { default as QuestionClassifier } from './QuestionClassifier' diff --git a/web/app/components/workflow/block-icon.tsx b/web/app/components/workflow/block-icon.tsx index 6bec704492..42497b8e9b 100644 --- a/web/app/components/workflow/block-icon.tsx +++ b/web/app/components/workflow/block-icon.tsx @@ -4,12 +4,14 @@ import { BlockEnum } from './types' import { Answer, Code, + DocsExtractor, End, Home, Http, IfElse, Iteration, KnowledgeRetrieval, + ListFilter, Llm, ParameterExtractor, QuestionClassifier, @@ -46,6 +48,8 @@ const getIcon = (type: BlockEnum, className: string) => { [BlockEnum.Tool]: , [BlockEnum.Iteration]: , [BlockEnum.ParameterExtractor]: , + [BlockEnum.DocExtractor]: , + [BlockEnum.ListFilter]: , }[type] } const ICON_CONTAINER_BG_COLOR_MAP: Record = { @@ -63,6 +67,8 @@ const ICON_CONTAINER_BG_COLOR_MAP: Record = { [BlockEnum.VariableAssigner]: 'bg-[#2E90FA]', [BlockEnum.VariableAggregator]: 'bg-[#2E90FA]', [BlockEnum.ParameterExtractor]: 'bg-[#2E90FA]', + [BlockEnum.DocExtractor]: 'bg-util-colors-green-green-500', + [BlockEnum.ListFilter]: 'bg-util-colors-cyan-cyan-500', } const BlockIcon: FC = ({ type, From f6caf0915b5521b29397566183226b5bfc35efbb Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 26 Jul 2024 14:23:34 +0800 Subject: [PATCH 005/275] chore: block bg to utils color --- web/app/components/workflow/block-icon.tsx | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/web/app/components/workflow/block-icon.tsx b/web/app/components/workflow/block-icon.tsx index 42497b8e9b..1127abae49 100644 --- a/web/app/components/workflow/block-icon.tsx +++ b/web/app/components/workflow/block-icon.tsx @@ -53,20 +53,20 @@ const getIcon = (type: BlockEnum, className: string) => { }[type] } const ICON_CONTAINER_BG_COLOR_MAP: Record = { - [BlockEnum.Start]: 'bg-primary-500', - [BlockEnum.LLM]: 'bg-[#6172F3]', - [BlockEnum.Code]: 'bg-[#2E90FA]', - [BlockEnum.End]: 'bg-[#F79009]', - [BlockEnum.IfElse]: 'bg-[#06AED4]', - [BlockEnum.Iteration]: 'bg-[#06AED4]', - [BlockEnum.HttpRequest]: 'bg-[#875BF7]', - [BlockEnum.Answer]: 'bg-[#F79009]', - [BlockEnum.KnowledgeRetrieval]: 'bg-[#16B364]', - [BlockEnum.QuestionClassifier]: 'bg-[#16B364]', - [BlockEnum.TemplateTransform]: 'bg-[#2E90FA]', - [BlockEnum.VariableAssigner]: 'bg-[#2E90FA]', - [BlockEnum.VariableAggregator]: 'bg-[#2E90FA]', - [BlockEnum.ParameterExtractor]: 'bg-[#2E90FA]', + [BlockEnum.Start]: 'bg-util-colors-blue-brand-blue-brand-500', + [BlockEnum.LLM]: 'bg-util-colors-indigo-indigo-500', + [BlockEnum.Code]: 'bg-util-colors-blue-blue-500', + [BlockEnum.End]: 'bg-util-colors-warning-warning-500', + [BlockEnum.IfElse]: 'bg-util-colors-cyan-cyan-500', + [BlockEnum.Iteration]: 'bg-util-colors-cyan-cyan-500', + [BlockEnum.HttpRequest]: 'bg-util-colors-violet-violet-500', + [BlockEnum.Answer]: 'bg-util-colors-warning-warning-500', + [BlockEnum.KnowledgeRetrieval]: 'bg-util-colors-green-green-500', + [BlockEnum.QuestionClassifier]: 'bg-util-colors-green-green-500', + [BlockEnum.TemplateTransform]: 'bg-util-colors-blue-blue-500', + [BlockEnum.VariableAssigner]: 'bg-util-colors-blue-blue-500', + [BlockEnum.VariableAggregator]: 'bg-util-colors-blue-blue-500', + [BlockEnum.ParameterExtractor]: 'bg-util-colors-blue-blue-500', [BlockEnum.DocExtractor]: 'bg-util-colors-green-green-500', [BlockEnum.ListFilter]: 'bg-util-colors-cyan-cyan-500', } From 6e15d7f777014cf7f22cc455ec3dc9b3d4e5a7ea Mon Sep 17 00:00:00 2001 From: Joel Date: Fri, 26 Jul 2024 15:00:29 +0800 Subject: [PATCH 006/275] feat: doc extract inputs --- .../workflow/nodes/doc-extractor/panel.tsx | 12 +++++++++++- web/i18n/en-US/workflow.ts | 4 ++++ web/i18n/zh-Hans/workflow.ts | 6 ++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/doc-extractor/panel.tsx b/web/app/components/workflow/nodes/doc-extractor/panel.tsx index 612220b811..b5177306c9 100644 --- a/web/app/components/workflow/nodes/doc-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/doc-extractor/panel.tsx @@ -2,6 +2,7 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' import VarReferencePicker from '../_base/components/variable/var-reference-picker' +import OutputVars, { VarItem } from '../_base/components/output-vars' import useConfig from './use-config' import type { DocExtractorNodeType } from './types' import Field from '@/app/components/workflow/nodes/_base/components/field' @@ -26,7 +27,7 @@ const Panel: FC> = ({
> = ({ />
+
+ + + +
) } diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 9b6f538380..180e4fb8ec 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -494,6 +494,10 @@ const translation = { }, }, docExtractor: { + inputVars: 'Input Variables', + outputVars: { + text: 'Extracted text', + }, }, }, tracing: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 5b1574ac6c..d5296f9f10 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -493,6 +493,12 @@ const translation = { showAuthor: '显示作者', }, }, + docExtractor: { + inputVars: '输入变量', + outputVars: { + text: '提取的文本', + }, + }, }, tracing: { stopBy: '由{{user}}终止', From 2498c238b29a7873a6d0a94cb4a8e67ffcf456f4 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 26 Jul 2024 16:54:25 +0800 Subject: [PATCH 007/275] file-uploader --- .../components/base/action-button/index.css | 2 +- .../base/chat/chat/chat-input-area/hooks.ts | 36 +++++++++++++ .../base/chat/chat/chat-input-area/index.tsx | 51 +++++++++++++++++++ .../file-list/file-list-flex/index.tsx | 9 ++++ .../file-list/file-list-full-width/index.tsx | 9 ++++ .../base/file-uploader/file-list/hooks.ts | 0 .../base/file-uploader/file-list/index.tsx | 0 .../file-uploader-in-chat-input/index.tsx | 51 +++++++++++++++++++ .../components/base/file-uploader/index.ts | 1 + 9 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 web/app/components/base/chat/chat/chat-input-area/hooks.ts create mode 100644 web/app/components/base/chat/chat/chat-input-area/index.tsx create mode 100644 web/app/components/base/file-uploader/file-list/file-list-flex/index.tsx create mode 100644 web/app/components/base/file-uploader/file-list/file-list-full-width/index.tsx create mode 100644 web/app/components/base/file-uploader/file-list/hooks.ts create mode 100644 web/app/components/base/file-uploader/file-list/index.tsx diff --git a/web/app/components/base/action-button/index.css b/web/app/components/base/action-button/index.css index b87dbc8d4a..5ed29742db 100644 --- a/web/app/components/base/action-button/index.css +++ b/web/app/components/base/action-button/index.css @@ -16,7 +16,7 @@ } .action-btn-l { - @apply p-1.5 w-[34px] h-[34px] rounded-lg + @apply p-1.5 w-8 h-8 rounded-lg } /* m is for the regular button */ diff --git a/web/app/components/base/chat/chat/chat-input-area/hooks.ts b/web/app/components/base/chat/chat/chat-input-area/hooks.ts new file mode 100644 index 0000000000..3559cb8799 --- /dev/null +++ b/web/app/components/base/chat/chat/chat-input-area/hooks.ts @@ -0,0 +1,36 @@ +import { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react' +import type { TextAreaRef } from 'rc-textarea' + +export const useTextAreaHeight = () => { + const textareaRef = useRef(null) + const [height, setHeight] = useState(0) + + const handleComputeHeight = () => { + const textareaElement = textareaRef.current?.resizableTextArea.textArea + if (textareaElement) { + const { height } = textareaElement.getBoundingClientRect() + + setHeight(height) + } + } + + useEffect(() => { + handleComputeHeight() + }, []) + + const handleTextareaResize = useCallback(() => { + handleComputeHeight() + }, []) + + return { + textareaRef, + handleTextareaResize, + isMultipleLine: useMemo(() => height > 32, [height]), + } +} diff --git a/web/app/components/base/chat/chat/chat-input-area/index.tsx b/web/app/components/base/chat/chat/chat-input-area/index.tsx new file mode 100644 index 0000000000..be49248cb6 --- /dev/null +++ b/web/app/components/base/chat/chat/chat-input-area/index.tsx @@ -0,0 +1,51 @@ +import { + memo, +} from 'react' +import Textarea from 'rc-textarea' +import { RiSendPlane2Fill } from '@remixicon/react' +import { useTextAreaHeight } from './hooks' +import Button from '@/app/components/base/button' +import { FileUploaderInChatInput } from '@/app/components/base/file-uploader' +import cn from '@/utils/classnames' + +const ChatInputArea = () => { + const { + textareaRef, + handleTextareaResize, + isMultipleLine, + } = useTextAreaHeight() + + return ( +
+ + ) + }, +) +Textarea.displayName = 'Textarea' + +export default Textarea +export { Textarea, textareaVariants } diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index 2a66f2a59a..41d78b8e33 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -12,6 +12,7 @@ import CodeEditor from '../editor/code-editor' import { CodeLanguage } from '../../../code/types' import TextEditor from '../editor/text-editor' import Select from '@/app/components/base/select' +import Textarea from '@/app/components/base/textarea' import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader' import { Resolution } from '@/types/app' import { useFeatures } from '@/app/components/base/features/hooks' @@ -116,10 +117,9 @@ const FormItem: FC = ({ { type === InputVarType.paragraph && ( - + />
{/* Available Tools */} diff --git a/web/app/components/tools/workflow-tool/index.tsx b/web/app/components/tools/workflow-tool/index.tsx index 0f9fe4c4c1..976e5ffb01 100644 --- a/web/app/components/tools/workflow-tool/index.tsx +++ b/web/app/components/tools/workflow-tool/index.tsx @@ -9,6 +9,7 @@ import produce from 'immer' import type { Emoji, WorkflowToolProviderParameter, WorkflowToolProviderRequest } from '../types' import cn from '@/utils/classnames' import Drawer from '@/app/components/base/drawer-plus' +import Textarea from '@/app/components/base/textarea' import Button from '@/app/components/base/button' import Toast from '@/app/components/base/toast' import EmojiPicker from '@/app/components/base/emoji-picker' @@ -172,8 +173,7 @@ const WorkflowToolAsModal: FC = ({ {/* description */}
{t('tools.createTool.description')}
- -
- ) - : ( -
- )} - {renderQuestions()} - ) : ( -
{t('appDebug.openingStatement.noDataPlaceHolder')}
- )} - - {isShowConfirmAddVar && ( - - )} - -
- - ) -} -export default React.memo(OpeningStatement) diff --git a/web/app/components/app/configuration/features/experience-enchance-group/index.tsx b/web/app/components/app/configuration/features/experience-enchance-group/index.tsx deleted file mode 100644 index 6902a17468..0000000000 --- a/web/app/components/app/configuration/features/experience-enchance-group/index.tsx +++ /dev/null @@ -1,43 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import GroupName from '../../base/group-name' -import TextToSpeech from '../chat-group/text-to-speech' -import MoreLikeThis from './more-like-this' - -/* -* Include -* 1. More like this -*/ - -type ExperienceGroupProps = { - isShowTextToSpeech: boolean - isShowMoreLike: boolean -} - -const ExperienceEnchanceGroup: FC = ({ - isShowTextToSpeech, - isShowMoreLike, -}) => { - const { t } = useTranslation() - - return ( -
- -
- { - isShowMoreLike && ( - - ) - } - { - isShowTextToSpeech && ( - - ) - } -
-
- ) -} -export default React.memo(ExperienceEnchanceGroup) diff --git a/web/app/components/app/configuration/features/experience-enchance-group/more-like-this/index.tsx b/web/app/components/app/configuration/features/experience-enchance-group/more-like-this/index.tsx deleted file mode 100644 index f63ed1c25a..0000000000 --- a/web/app/components/app/configuration/features/experience-enchance-group/more-like-this/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import { XMarkIcon } from '@heroicons/react/24/outline' -import { useLocalStorageState } from 'ahooks' -import MoreLikeThisIcon from '../../../base/icons/more-like-this-icon' -import Panel from '@/app/components/app/configuration/base/feature-panel' - -const GENERATE_NUM = 1 - -const warningIcon = ( - - - - -) -const MoreLikeThis: FC = () => { - const { t } = useTranslation() - - const [isHideTip, setIsHideTip] = useLocalStorageState('isHideMoreLikeThisTip', { - defaultValue: false, - }) - - const headerRight = ( -
{t('appDebug.feature.moreLikeThis.generateNumTip')} {GENERATE_NUM}
- ) - return ( - } - headerRight={headerRight} - noBodySpacing - > - {!isHideTip && ( -
-
-
{warningIcon}
-
{t('appDebug.feature.moreLikeThis.tip')}
-
-
setIsHideTip(true)}> - -
-
- )} - -
- ) -} -export default React.memo(MoreLikeThis) diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 2ec0c0b58d..7a5aa26a56 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -466,6 +466,12 @@ const Configuration: FC = () => { if (flag) formattingChangedDispatcher() }, [formattingChangedDispatcher, setShowAppConfigureFeaturesModal]) + const handleAddPromptVariable = useCallback((variable: PromptVariable[]) => { + const newModelConfig = produce(modelConfig, (draft: ModelConfig) => { + draft.configs.prompt_variables = variable + }) + setModelConfig(newModelConfig) + }, [modelConfig]) useEffect(() => { (async () => { @@ -994,6 +1000,8 @@ const Configuration: FC = () => { disabled={false} onChange={handleFeaturesChange} onClose={() => setShowAppConfigureFeaturesModal(false)} + promptVariables={modelConfig.configs.prompt_variables} + onAutoAddPromptVariable={handleAddPromptVariable} /> )} diff --git a/web/app/components/app/configuration/toolbox/index.tsx b/web/app/components/app/configuration/toolbox/index.tsx deleted file mode 100644 index 488ca86b90..0000000000 --- a/web/app/components/app/configuration/toolbox/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -'use client' - -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import GroupName from '../base/group-name' -import Moderation from './moderation' -import Annotation from './annotation/config-param' -import type { EmbeddingModelConfig } from '@/app/components/app/annotation/type' - -export type ToolboxProps = { - showModerationSettings: boolean - showAnnotation: boolean - onEmbeddingChange: (embeddingModel: EmbeddingModelConfig) => void - onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void -} - -const Toolbox: FC = ({ - showModerationSettings, - showAnnotation, - onEmbeddingChange, - onScoreChange, -}) => { - const { t } = useTranslation() - - return ( -
- - { - showModerationSettings && ( - - ) - } - { - (showAnnotation || true) && ( - - ) - } -
- ) -} -export default React.memo(Toolbox) diff --git a/web/app/components/app/configuration/toolbox/moderation/index.tsx b/web/app/components/app/configuration/toolbox/moderation/index.tsx deleted file mode 100644 index 9eb14e98d2..0000000000 --- a/web/app/components/app/configuration/toolbox/moderation/index.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useTranslation } from 'react-i18next' -import useSWR from 'swr' -import { useContext } from 'use-context-selector' -import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { useModalContext } from '@/context/modal-context' -import ConfigContext from '@/context/debug-configuration' -import { fetchCodeBasedExtensionList } from '@/service/common' -import I18n from '@/context/i18n' -const Moderation = () => { - const { t } = useTranslation() - const { setShowModerationSettingModal } = useModalContext() - const { locale } = useContext(I18n) - const { - moderationConfig, - setModerationConfig, - } = useContext(ConfigContext) - const { data: codeBasedExtensionList } = useSWR( - '/code-based-extension?module=moderation', - fetchCodeBasedExtensionList, - ) - - const handleOpenModerationSettingModal = () => { - setShowModerationSettingModal({ - payload: moderationConfig, - onSaveCallback: setModerationConfig, - }) - } - - const renderInfo = () => { - let prefix = '' - let suffix = '' - if (moderationConfig.type === 'openai_moderation') - prefix = t('appDebug.feature.moderation.modal.provider.openai') - else if (moderationConfig.type === 'keywords') - prefix = t('appDebug.feature.moderation.modal.provider.keywords') - else if (moderationConfig.type === 'api') - prefix = t('common.apiBasedExtension.selector.title') - else - prefix = codeBasedExtensionList?.data.find(item => item.name === moderationConfig.type)?.label[locale] || '' - - if (moderationConfig.config?.inputs_config?.enabled && moderationConfig.config?.outputs_config?.enabled) - suffix = t('appDebug.feature.moderation.allEnabled') - else if (moderationConfig.config?.inputs_config?.enabled) - suffix = t('appDebug.feature.moderation.inputEnabled') - else if (moderationConfig.config?.outputs_config?.enabled) - suffix = t('appDebug.feature.moderation.outputEnabled') - - return `${prefix} · ${suffix}` - } - - return ( -
-
- -
-
- {t('appDebug.feature.moderation.title')} -
-
- {renderInfo()} -
-
-
- - {t('common.operation.settings')} -
-
- ) -} - -export default Moderation diff --git a/web/app/components/base/features/feature-panel/citation/index.tsx b/web/app/components/base/features/feature-panel/citation/index.tsx deleted file mode 100644 index 4003b68cd3..0000000000 --- a/web/app/components/base/features/feature-panel/citation/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'use client' -import React, { type FC } from 'react' -import { useTranslation } from 'react-i18next' -import Panel from '@/app/components/app/configuration/base/feature-panel' -import { Citations } from '@/app/components/base/icons/src/vender/solid/editor' - -const Citation: FC = () => { - const { t } = useTranslation() - - return ( - -
{t('appDebug.feature.citation.title')}
-
- } - headerIcon={} - headerRight={ -
{t('appDebug.feature.citation.resDes')}
- } - noBodySpacing - /> - ) -} -export default React.memo(Citation) diff --git a/web/app/components/base/features/feature-panel/file-upload/index.tsx b/web/app/components/base/features/feature-panel/file-upload/index.tsx deleted file mode 100644 index 3ea940ca16..0000000000 --- a/web/app/components/base/features/feature-panel/file-upload/index.tsx +++ /dev/null @@ -1,63 +0,0 @@ -'use client' -import produce from 'immer' -import React, { useCallback } from 'react' -import { useTranslation } from 'react-i18next' -import type { OnFeaturesChange } from '../../types' -import { - useFeatures, - useFeaturesStore, -} from '../../hooks' -import ParamConfig from './param-config' -import Switch from '@/app/components/base/switch' -import { File05 } from '@/app/components/base/icons/src/vender/solid/files' - -type FileUploadProps = { - onChange?: OnFeaturesChange - disabled?: boolean -} -const FileUpload = ({ - onChange, - disabled, -}: FileUploadProps) => { - const { t } = useTranslation() - const featuresStore = useFeaturesStore() - const file = useFeatures(s => s.features.file) - - const handleSwitch = useCallback((value: boolean) => { - const { - features, - setFeatures, - } = featuresStore!.getState() - const newFeatures = produce(features, (draft) => { - if (draft.file?.image) - draft.file.image.enabled = value - }) - setFeatures(newFeatures) - - if (onChange) - onChange(newFeatures) - }, [featuresStore, onChange]) - - return ( -
-
- -
-
- {t('common.imageUploader.imageUpload')} -
-
-
- -
- -
-
- ) -} -export default React.memo(FileUpload) diff --git a/web/app/components/base/features/feature-panel/file-upload/param-config-content.tsx b/web/app/components/base/features/feature-panel/file-upload/param-config-content.tsx deleted file mode 100644 index b481420aaa..0000000000 --- a/web/app/components/base/features/feature-panel/file-upload/param-config-content.tsx +++ /dev/null @@ -1,119 +0,0 @@ -'use client' - -import produce from 'immer' -import React, { useCallback, useMemo } from 'react' -import { useTranslation } from 'react-i18next' -import type { OnFeaturesChange } from '../../types' -import { - useFeatures, - useFeaturesStore, -} from '../../hooks' -import RadioGroup from './radio-group' -import { TransferMethod } from '@/types/app' -import ParamItem from '@/app/components/base/param-item' - -const MIN = 1 -const MAX = 6 -type ParamConfigContentProps = { - onChange?: OnFeaturesChange -} -const ParamConfigContent = ({ - onChange, -}: ParamConfigContentProps) => { - const { t } = useTranslation() - const featuresStore = useFeaturesStore() - const file = useFeatures(s => s.features.file) - - const transferMethod = useMemo(() => { - if (!file?.image?.transfer_methods || file?.image.transfer_methods.length === 2) - return TransferMethod.all - - return file.image.transfer_methods[0] - }, [file?.image?.transfer_methods]) - - const handleTransferMethodsChange = useCallback((value: TransferMethod) => { - const { - features, - setFeatures, - } = featuresStore!.getState() - const newFeatures = produce(features, (draft) => { - if (draft.file?.image) { - if (value === TransferMethod.all) - draft.file.image.transfer_methods = [TransferMethod.remote_url, TransferMethod.local_file] - else - draft.file.image.transfer_methods = [value] - } - }) - setFeatures(newFeatures) - if (onChange) - onChange(newFeatures) - }, [featuresStore, onChange]) - - const handleLimitsChange = useCallback((_key: string, value: number) => { - if (!value) - return - - const { - features, - setFeatures, - } = featuresStore!.getState() - const newFeatures = produce(features, (draft) => { - if (draft.file?.image) - draft.file.image.number_limits = value - }) - setFeatures(newFeatures) - if (onChange) - onChange(newFeatures) - }, [featuresStore, onChange]) - - return ( -
-
-
{t('common.operation.settings')}
-
-
-
{t('appDebug.vision.visionSettings.uploadMethod')}
- -
-
- -
-
-
-
- ) -} - -export default React.memo(ParamConfigContent) diff --git a/web/app/components/base/features/feature-panel/file-upload/param-config.tsx b/web/app/components/base/features/feature-panel/file-upload/param-config.tsx deleted file mode 100644 index 805fe8fb3e..0000000000 --- a/web/app/components/base/features/feature-panel/file-upload/param-config.tsx +++ /dev/null @@ -1,49 +0,0 @@ -'use client' - -import { memo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import type { OnFeaturesChange } from '../../types' -import ParamConfigContent from './param-config-content' -import cn from '@/utils/classnames' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' - -type ParamsConfigProps = { - onChange?: OnFeaturesChange - disabled?: boolean -} -const ParamsConfig = ({ - onChange, - disabled, -}: ParamsConfigProps) => { - const { t } = useTranslation() - const [open, setOpen] = useState(false) - - return ( - - !disabled && setOpen(v => !v)}> -
- -
{t('appDebug.voice.settings')}
-
-
- -
- -
-
-
- ) -} -export default memo(ParamsConfig) diff --git a/web/app/components/base/features/feature-panel/file-upload/radio-group/index.tsx b/web/app/components/base/features/feature-panel/file-upload/radio-group/index.tsx deleted file mode 100644 index a1cfb06e6a..0000000000 --- a/web/app/components/base/features/feature-panel/file-upload/radio-group/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import s from './style.module.css' -import cn from '@/utils/classnames' - -type OPTION = { - label: string - value: any -} - -type Props = { - className?: string - options: OPTION[] - value: any - onChange: (value: any) => void -} - -const RadioGroup: FC = ({ - className = '', - options, - value, - onChange, -}) => { - return ( -
- {options.map(item => ( -
onChange(item.value)} - > -
-
{item.label}
-
- ))} -
- ) -} -export default React.memo(RadioGroup) diff --git a/web/app/components/base/features/feature-panel/file-upload/radio-group/style.module.css b/web/app/components/base/features/feature-panel/file-upload/radio-group/style.module.css deleted file mode 100644 index 22c29c6a42..0000000000 --- a/web/app/components/base/features/feature-panel/file-upload/radio-group/style.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.item { - @apply grow flex items-center h-8 px-2.5 rounded-lg bg-gray-25 border border-gray-100 cursor-pointer space-x-2; -} - -.item:hover { - background-color: #ffffff; - border-color: #B2CCFF; - box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03); -} - -.item.checked { - background-color: #ffffff; - border-color: #528BFF; - box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.10); -} - -.radio { - @apply w-4 h-4 border-[2px] border-gray-200 rounded-full; -} - -.item.checked .radio { - border-width: 5px; - border-color: #155eef; -} \ No newline at end of file diff --git a/web/app/components/base/features/feature-panel/index.tsx b/web/app/components/base/features/feature-panel/index.tsx deleted file mode 100644 index e979391c92..0000000000 --- a/web/app/components/base/features/feature-panel/index.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import { - memo, - useMemo, -} from 'react' -import { useTranslation } from 'react-i18next' -import type { OnFeaturesChange } from '../types' -import { useFeatures } from '../hooks' -import FileUpload from './file-upload' -import OpeningStatement from './opening-statement' -import type { OpeningStatementProps } from './opening-statement' -import SuggestedQuestionsAfterAnswer from './suggested-questions-after-answer' -import TextToSpeech from './text-to-speech' -import SpeechToText from './speech-to-text' -import Citation from './citation' -import Moderation from './moderation' - -export type FeaturePanelProps = { - onChange?: OnFeaturesChange - openingStatementProps: OpeningStatementProps - disabled?: boolean -} -const FeaturePanel = ({ - onChange, - openingStatementProps, - disabled, -}: FeaturePanelProps) => { - const { t } = useTranslation() - const features = useFeatures(s => s.features) - - const showAdvanceFeature = useMemo(() => { - return features.opening?.enabled || features.suggested?.enabled || features.speech2text?.enabled || features.text2speech?.enabled || features.citation?.enabled - }, [features]) - - const showToolFeature = useMemo(() => { - return features.moderation?.enabled - }, [features]) - - return ( -
- - { - showAdvanceFeature && ( -
-
-
- {t('appDebug.feature.groupChat.title')} -
-
-
-
- { - features.opening?.enabled && ( - - ) - } - { - features.suggested?.enabled && ( - - ) - } - { - features.text2speech?.enabled && ( - - ) - } - { - features.speech2text?.enabled && ( - - ) - } - { - features.citation?.enabled && ( - - ) - } -
-
- ) - } - { - showToolFeature && ( -
-
-
- {t('appDebug.feature.groupChat.title')} -
-
-
-
- { - features.moderation?.enabled && ( - - ) - } -
-
- ) - } -
- ) -} -export default memo(FeaturePanel) diff --git a/web/app/components/base/features/feature-panel/moderation/index.tsx b/web/app/components/base/features/feature-panel/moderation/index.tsx deleted file mode 100644 index 0a473ccd06..0000000000 --- a/web/app/components/base/features/feature-panel/moderation/index.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import { memo } from 'react' -import { useTranslation } from 'react-i18next' -import useSWR from 'swr' -import produce from 'immer' -import { useContext } from 'use-context-selector' -import { - useFeatures, - useFeaturesStore, -} from '../../hooks' -import type { OnFeaturesChange } from '../../types' -import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { useModalContext } from '@/context/modal-context' -import { fetchCodeBasedExtensionList } from '@/service/common' -import I18n from '@/context/i18n' - -type ModerationProps = { - onChange?: OnFeaturesChange - disabled?: boolean -} -const Moderation = ({ - onChange, - disabled, -}: ModerationProps) => { - const { t } = useTranslation() - const { setShowModerationSettingModal } = useModalContext() - const { locale } = useContext(I18n) - const featuresStore = useFeaturesStore() - const moderation = useFeatures(s => s.features.moderation) - - const { data: codeBasedExtensionList } = useSWR( - '/code-based-extension?module=moderation', - fetchCodeBasedExtensionList, - ) - - const handleOpenModerationSettingModal = () => { - if (disabled) - return - - const { - features, - setFeatures, - } = featuresStore!.getState() - setShowModerationSettingModal({ - payload: moderation as any, - onSaveCallback: (newModeration) => { - const newFeatures = produce(features, (draft) => { - draft.moderation = newModeration - }) - setFeatures(newFeatures) - if (onChange) - onChange(newFeatures) - }, - }) - } - - const renderInfo = () => { - let prefix = '' - let suffix = '' - if (moderation?.type === 'openai_moderation') - prefix = t('appDebug.feature.moderation.modal.provider.openai') - else if (moderation?.type === 'keywords') - prefix = t('appDebug.feature.moderation.modal.provider.keywords') - else if (moderation?.type === 'api') - prefix = t('common.apiBasedExtension.selector.title') - else - prefix = codeBasedExtensionList?.data.find(item => item.name === moderation?.type)?.label[locale] || '' - - if (moderation?.config?.inputs_config?.enabled && moderation.config?.outputs_config?.enabled) - suffix = t('appDebug.feature.moderation.allEnabled') - else if (moderation?.config?.inputs_config?.enabled) - suffix = t('appDebug.feature.moderation.inputEnabled') - else if (moderation?.config?.outputs_config?.enabled) - suffix = t('appDebug.feature.moderation.outputEnabled') - - return `${prefix} · ${suffix}` - } - - return ( -
-
- -
-
- {t('appDebug.feature.moderation.title')} -
-
- {renderInfo()} -
-
-
- - {t('common.operation.settings')} -
-
- ) -} - -export default memo(Moderation) diff --git a/web/app/components/base/features/feature-panel/opening-statement/index.tsx b/web/app/components/base/features/feature-panel/opening-statement/index.tsx deleted file mode 100644 index 54bf8bd937..0000000000 --- a/web/app/components/base/features/feature-panel/opening-statement/index.tsx +++ /dev/null @@ -1,321 +0,0 @@ -/* eslint-disable multiline-ternary */ -'use client' -import type { FC } from 'react' -import React, { useEffect, useRef, useState } from 'react' -import produce from 'immer' -import { - RiAddLine, - RiDeleteBinLine, -} from '@remixicon/react' -import { useTranslation } from 'react-i18next' -import { useBoolean } from 'ahooks' -import { ReactSortable } from 'react-sortablejs' -import { - useFeatures, - useFeaturesStore, -} from '../../hooks' -import type { OnFeaturesChange } from '../../types' -import cn from '@/utils/classnames' -import Panel from '@/app/components/app/configuration/base/feature-panel' -import Button from '@/app/components/base/button' -import OperationBtn from '@/app/components/app/configuration/base/operation-btn' -import { getInputKeys } from '@/app/components/base/block-input' -import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var' -import { getNewVar } from '@/utils/var' -import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight' -import type { PromptVariable } from '@/models/debug' - -const MAX_QUESTION_NUM = 5 - -export type OpeningStatementProps = { - onChange?: OnFeaturesChange - readonly?: boolean - promptVariables?: PromptVariable[] - onAutoAddPromptVariable: (variable: PromptVariable[]) => void -} - -// regex to match the {{}} and replace it with a span -const regex = /\{\{([^}]+)\}\}/g - -const OpeningStatement: FC = ({ - onChange, - readonly, - promptVariables = [], - onAutoAddPromptVariable, -}) => { - const { t } = useTranslation() - const featureStore = useFeaturesStore() - const openingStatement = useFeatures(s => s.features.opening) - const value = openingStatement?.opening_statement || '' - const suggestedQuestions = openingStatement?.suggested_questions || [] - const [notIncludeKeys, setNotIncludeKeys] = useState([]) - - const hasValue = !!(value || '').trim() - const inputRef = useRef(null) - - const [isFocus, { setTrue: didSetFocus, setFalse: setBlur }] = useBoolean(false) - - const setFocus = () => { - didSetFocus() - setTimeout(() => { - const input = inputRef.current - if (input) { - input.focus() - input.setSelectionRange(input.value.length, input.value.length) - } - }, 0) - } - - const [tempValue, setTempValue] = useState(value) - useEffect(() => { - setTempValue(value || '') - }, [value]) - - const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(suggestedQuestions || []) - const notEmptyQuestions = tempSuggestedQuestions.filter(question => !!question && question.trim()) - const coloredContent = (tempValue || '') - .replace(//g, '>') - .replace(regex, varHighlightHTML({ name: '$1' })) // `{{$1}}` - .replace(/\n/g, '
') - - const handleEdit = () => { - if (readonly) - return - setFocus() - } - - const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false) - - const handleCancel = () => { - setBlur() - setTempValue(value) - setTempSuggestedQuestions(suggestedQuestions) - } - - const handleConfirm = () => { - const keys = getInputKeys(tempValue) - const promptKeys = promptVariables.map(item => item.key) - let notIncludeKeys: string[] = [] - - if (promptKeys.length === 0) { - if (keys.length > 0) - notIncludeKeys = keys - } - else { - notIncludeKeys = keys.filter(key => !promptKeys.includes(key)) - } - - if (notIncludeKeys.length > 0) { - setNotIncludeKeys(notIncludeKeys) - showConfirmAddVar() - return - } - setBlur() - const { getState } = featureStore! - const { - features, - setFeatures, - } = getState() - - const newFeatures = produce(features, (draft) => { - if (draft.opening) { - draft.opening.opening_statement = tempValue - draft.opening.suggested_questions = tempSuggestedQuestions - } - }) - setFeatures(newFeatures) - - if (onChange) - onChange(newFeatures) - } - - const cancelAutoAddVar = () => { - const { getState } = featureStore! - const { - features, - setFeatures, - } = getState() - - const newFeatures = produce(features, (draft) => { - if (draft.opening) - draft.opening.opening_statement = tempValue - }) - setFeatures(newFeatures) - - if (onChange) - onChange(newFeatures) - hideConfirmAddVar() - setBlur() - } - - const autoAddVar = () => { - const { getState } = featureStore! - const { - features, - setFeatures, - } = getState() - - const newFeatures = produce(features, (draft) => { - if (draft.opening) - draft.opening.opening_statement = tempValue - }) - setFeatures(newFeatures) - if (onChange) - onChange(newFeatures) - onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))]) - hideConfirmAddVar() - setBlur() - } - - const headerRight = !readonly ? ( - isFocus ? ( -
- - -
- ) : ( - - ) - ) : null - - const renderQuestions = () => { - return isFocus ? ( -
-
-
-
{t('appDebug.openingStatement.openingQuestion')}
-
·
-
{tempSuggestedQuestions.length}/{MAX_QUESTION_NUM}
-
-
-
- { - return { - id: index, - name, - } - })} - setList={list => setTempSuggestedQuestions(list.map(item => item.name))} - handle='.handle' - ghostClass="opacity-50" - animation={150} - > - {tempSuggestedQuestions.map((question, index) => { - return ( -
-
- - - -
- { - const value = e.target.value - setTempSuggestedQuestions(tempSuggestedQuestions.map((item, i) => { - if (index === i) - return value - - return item - })) - }} - className={'w-full overflow-x-auto pl-1.5 pr-8 text-sm leading-9 text-gray-900 border-0 grow h-9 bg-transparent focus:outline-none cursor-pointer rounded-lg'} - /> - -
{ - setTempSuggestedQuestions(tempSuggestedQuestions.filter((_, i) => index !== i)) - }} - > - -
-
- ) - })}
- {tempSuggestedQuestions.length < MAX_QUESTION_NUM && ( -
{ setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }} - className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'> - -
{t('appDebug.variableConig.addOption')}
-
- )} -
- ) : ( -
- {notEmptyQuestions.map((question, index) => { - return ( -
- {question} -
- ) - })} -
- ) - } - - return ( - - - - } - headerRight={headerRight} - hasHeaderBottomBorder={!hasValue} - isFocus={isFocus} - > -
- {(hasValue || (!hasValue && isFocus)) ? ( - <> - {isFocus - ? ( -
- -
- ) - : ( -
- )} - {renderQuestions()} - ) : ( -
{t('appDebug.openingStatement.noDataPlaceHolder')}
- )} - - {isShowConfirmAddVar && ( - - )} - -
-
- ) -} -export default React.memo(OpeningStatement) diff --git a/web/app/components/base/features/feature-panel/score-slider/base-slider/index.tsx b/web/app/components/base/features/feature-panel/score-slider/base-slider/index.tsx deleted file mode 100644 index 2e08a99122..0000000000 --- a/web/app/components/base/features/feature-panel/score-slider/base-slider/index.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import ReactSlider from 'react-slider' -import s from './style.module.css' -import cn from '@/utils/classnames' - -type ISliderProps = { - className?: string - value: number - max?: number - min?: number - step?: number - disabled?: boolean - onChange: (value: number) => void -} - -const Slider: React.FC = ({ className, max, min, step, value, disabled, onChange }) => { - return ( -
-
-
- {(state.valueNow / 100).toFixed(2)} -
-
-
- )} - /> -} - -export default Slider diff --git a/web/app/components/base/features/feature-panel/score-slider/base-slider/style.module.css b/web/app/components/base/features/feature-panel/score-slider/base-slider/style.module.css deleted file mode 100644 index 4e93b39563..0000000000 --- a/web/app/components/base/features/feature-panel/score-slider/base-slider/style.module.css +++ /dev/null @@ -1,20 +0,0 @@ -.slider { - position: relative; -} - -.slider.disabled { - opacity: 0.6; -} - -.slider-thumb:focus { - outline: none; -} - -.slider-track { - background-color: #528BFF; - height: 2px; -} - -.slider-track-1 { - background-color: #E5E7EB; -} \ No newline at end of file diff --git a/web/app/components/base/features/feature-panel/score-slider/index.tsx b/web/app/components/base/features/feature-panel/score-slider/index.tsx deleted file mode 100644 index 9826cbadcf..0000000000 --- a/web/app/components/base/features/feature-panel/score-slider/index.tsx +++ /dev/null @@ -1,46 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import Slider from '@/app/components/app/configuration/toolbox/score-slider/base-slider' - -type Props = { - className?: string - value: number - onChange: (value: number) => void -} - -const ScoreSlider: FC = ({ - className, - value, - onChange, -}) => { - const { t } = useTranslation() - - return ( -
-
- -
-
-
-
0.8
-
·
-
{t('appDebug.feature.annotation.scoreThreshold.easyMatch')}
-
-
-
1.0
-
·
-
{t('appDebug.feature.annotation.scoreThreshold.accurateMatch')}
-
-
-
- ) -} -export default React.memo(ScoreSlider) diff --git a/web/app/components/base/features/feature-panel/speech-to-text/index.tsx b/web/app/components/base/features/feature-panel/speech-to-text/index.tsx deleted file mode 100644 index 2e5e3de439..0000000000 --- a/web/app/components/base/features/feature-panel/speech-to-text/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -'use client' -import React, { type FC } from 'react' -import { useTranslation } from 'react-i18next' -import { Microphone01 } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' - -const SpeechToTextConfig: FC = () => { - const { t } = useTranslation() - - return ( -
-
- -
-
-
{t('appDebug.feature.speechToText.title')}
-
-
-
{t('appDebug.feature.speechToText.resDes')}
-
- ) -} -export default React.memo(SpeechToTextConfig) diff --git a/web/app/components/base/features/feature-panel/suggested-questions-after-answer/index.tsx b/web/app/components/base/features/feature-panel/suggested-questions-after-answer/index.tsx deleted file mode 100644 index e6d0b6e7e0..0000000000 --- a/web/app/components/base/features/feature-panel/suggested-questions-after-answer/index.tsx +++ /dev/null @@ -1,25 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useTranslation } from 'react-i18next' -import { MessageSmileSquare } from '@/app/components/base/icons/src/vender/solid/communication' -import Tooltip from '@/app/components/base/tooltip' - -const SuggestedQuestionsAfterAnswer: FC = () => { - const { t } = useTranslation() - - return ( -
-
- -
-
-
{t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
- -
-
-
{t('appDebug.feature.suggestedQuestionsAfterAnswer.resDes')}
-
- ) -} -export default React.memo(SuggestedQuestionsAfterAnswer) diff --git a/web/app/components/base/features/feature-panel/text-to-speech/index.tsx b/web/app/components/base/features/feature-panel/text-to-speech/index.tsx deleted file mode 100644 index 2480a19077..0000000000 --- a/web/app/components/base/features/feature-panel/text-to-speech/index.tsx +++ /dev/null @@ -1,62 +0,0 @@ -'use client' -import useSWR from 'swr' -import React from 'react' -import { useTranslation } from 'react-i18next' -import { usePathname } from 'next/navigation' -import { useFeatures } from '../../hooks' -import type { OnFeaturesChange } from '../../types' -import ParamsConfig from './params-config' -import { Speaker } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices' -import { languages } from '@/i18n/language' -import { fetchAppVoices } from '@/service/apps' -import AudioBtn from '@/app/components/base/audio-btn' - -type TextToSpeechProps = { - onChange?: OnFeaturesChange - disabled?: boolean -} -const TextToSpeech = ({ - onChange, - disabled, -}: TextToSpeechProps) => { - const { t } = useTranslation() - const textToSpeech = useFeatures(s => s.features.text2speech) - - const pathname = usePathname() - const matched = pathname.match(/\/app\/([^/]+)/) - const appId = (matched?.length && matched[1]) ? matched[1] : '' - const language = textToSpeech?.language - const languageInfo = languages.find(i => i.value === textToSpeech?.language) - - const voiceItems = useSWR({ appId, language }, fetchAppVoices).data - const voiceItem = voiceItems?.find(item => item.value === textToSpeech?.voice) - - return ( -
-
- -
-
- {t('appDebug.feature.textToSpeech.title')} -
-
-
-
- {languageInfo && (`${languageInfo?.name} - `)}{voiceItem?.name ?? t('appDebug.voice.defaultDisplay')} - { languageInfo?.example && ( - - )} -
-
- -
-
- ) -} -export default React.memo(TextToSpeech) diff --git a/web/app/components/base/features/feature-panel/text-to-speech/param-config-content.tsx b/web/app/components/base/features/feature-panel/text-to-speech/param-config-content.tsx deleted file mode 100644 index e923d9a333..0000000000 --- a/web/app/components/base/features/feature-panel/text-to-speech/param-config-content.tsx +++ /dev/null @@ -1,241 +0,0 @@ -'use client' -import useSWR from 'swr' -import produce from 'immer' -import React, { Fragment } from 'react' -import { usePathname } from 'next/navigation' -import { useTranslation } from 'react-i18next' -import { Listbox, Transition } from '@headlessui/react' -import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid' -import { - useFeatures, - useFeaturesStore, -} from '../../hooks' -import type { OnFeaturesChange } from '../../types' -import classNames from '@/utils/classnames' -import type { Item } from '@/app/components/base/select' -import { fetchAppVoices } from '@/service/apps' -import Tooltip from '@/app/components/base/tooltip' -import { languages } from '@/i18n/language' -import RadioGroup from '@/app/components/app/configuration/config-vision/radio-group' -import { TtsAutoPlay } from '@/types/app' - -type VoiceParamConfigProps = { - onChange?: OnFeaturesChange -} -const VoiceParamConfig = ({ - onChange, -}: VoiceParamConfigProps) => { - const { t } = useTranslation() - const pathname = usePathname() - const matched = pathname.match(/\/app\/([^/]+)/) - const appId = (matched?.length && matched[1]) ? matched[1] : '' - const text2speech = useFeatures(state => state.features.text2speech) - const featuresStore = useFeaturesStore() - - let languageItem = languages.find(item => item.value === text2speech?.language) - if (languages && !languageItem) - languageItem = languages[0] - const localLanguagePlaceholder = languageItem?.name || t('common.placeholder.select') - - const language = languageItem?.value - const voiceItems = useSWR({ appId, language }, fetchAppVoices).data - let voiceItem = voiceItems?.find(item => item.value === text2speech?.voice) - if (voiceItems && !voiceItem) - voiceItem = voiceItems[0] - const localVoicePlaceholder = voiceItem?.name || t('common.placeholder.select') - - const handleChange = (value: Record) => { - const { - features, - setFeatures, - } = featuresStore!.getState() - - const newFeatures = produce(features, (draft) => { - draft.text2speech = { - ...draft.text2speech, - ...value, - } - }) - - setFeatures(newFeatures) - if (onChange) - onChange(newFeatures) - } - - return ( -
-
-
{t('appDebug.voice.voiceSettings.title')}
-
-
-
-
{t('appDebug.voice.voiceSettings.language')}
- - {t('appDebug.voice.voiceSettings.resolutionTooltip').split('\n').map(item => ( -
{item} -
- ))} -
- } - /> -
- { - handleChange({ - language: String(value.value), - }) - }} - > -
- - - {languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder} - - - - - - - - {languages.map((item: Item) => ( - - `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' - }` - } - value={item} - disabled={false} - > - {({ /* active, */ selected }) => ( - <> - {t(`common.voice.language.${(item.value).toString().replace('-', '')}`)} - {(selected || item.value === text2speech?.language) && ( - - - )} - - )} - - ))} - - -
-
-
- -
-
{t('appDebug.voice.voiceSettings.voice')}
- { - handleChange({ - voice: String(value.value), - }) - }} - > -
- - {voiceItem?.name ?? localVoicePlaceholder} - - - - - - - {voiceItems?.map((item: Item) => ( - - `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' - }` - } - value={item} - disabled={false} - > - {({ /* active, */ selected }) => ( - <> - {item.name} - {(selected || item.value === text2speech?.voice) && ( - - - )} - - )} - - ))} - - -
-
-
-
-
{t('appDebug.voice.voiceSettings.autoPlay')}
- { - handleChange({ - autoPlay: value, - }) - }} - /> -
-
-
-
- ) -} - -export default React.memo(VoiceParamConfig) diff --git a/web/app/components/base/features/feature-panel/text-to-speech/params-config.tsx b/web/app/components/base/features/feature-panel/text-to-speech/params-config.tsx deleted file mode 100644 index 095fd6cce8..0000000000 --- a/web/app/components/base/features/feature-panel/text-to-speech/params-config.tsx +++ /dev/null @@ -1,48 +0,0 @@ -'use client' -import { memo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import type { OnFeaturesChange } from '../../types' -import ParamConfigContent from './param-config-content' -import cn from '@/utils/classnames' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' - -type ParamsConfigProps = { - onChange?: OnFeaturesChange - disabled?: boolean -} -const ParamsConfig = ({ - onChange, - disabled, -}: ParamsConfigProps) => { - const { t } = useTranslation() - const [open, setOpen] = useState(false) - - return ( - - !disabled && setOpen(v => !v)}> -
- -
{t('appDebug.voice.settings')}
-
-
- -
- -
-
-
- ) -} -export default memo(ParamsConfig) diff --git a/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx b/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx index db7bad4d18..1e7c141059 100644 --- a/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx +++ b/web/app/components/base/features/new-feature-panel/conversation-opener/index.tsx @@ -6,18 +6,23 @@ import { LoveMessage } from '@/app/components/base/icons/src/vender/features' import FeatureCard from '@/app/components/base/features/new-feature-panel/feature-card' import Button from '@/app/components/base/button' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' -import type { OnFeaturesChange, OpeningStatement } from '@/app/components/base/features/types' +import type { OnFeaturesChange } from '@/app/components/base/features/types' import { FeatureEnum } from '@/app/components/base/features/types' import { useModalContext } from '@/context/modal-context' +import type { PromptVariable } from '@/models/debug' type Props = { disabled?: boolean onChange?: OnFeaturesChange + promptVariables?: PromptVariable[] + onAutoAddPromptVariable?: (variable: PromptVariable[]) => void } const ConversationOpener = ({ disabled, onChange, + promptVariables, + onAutoAddPromptVariable, }: Props) => { const { t } = useTranslation() const { setShowOpeningModal } = useModalContext() @@ -32,7 +37,11 @@ const ConversationOpener = ({ setFeatures, } = featuresStore!.getState() setShowOpeningModal({ - payload: opening as OpeningStatement, + payload: { + ...opening, + promptVariables, + onAutoAddPromptVariable, + }, onSaveCallback: (newOpening) => { const newFeatures = produce(features, (draft) => { draft.opening = newOpening @@ -46,7 +55,7 @@ const ConversationOpener = ({ onChange() }, }) - }, [disabled, featuresStore, onChange, opening, setShowOpeningModal]) + }, [disabled, featuresStore, onAutoAddPromptVariable, onChange, opening, promptVariables, setShowOpeningModal]) const handleChange = useCallback((type: FeatureEnum, enabled: boolean) => { const { diff --git a/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx b/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx index 6706b3699b..9f158695c7 100644 --- a/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx +++ b/web/app/components/base/features/new-feature-panel/conversation-opener/modal.tsx @@ -1,16 +1,23 @@ -import React, { useEffect, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' +import { useBoolean } from 'ahooks' import produce from 'immer' import { ReactSortable } from 'react-sortablejs' import { RiAddLine, RiAsterisk, RiCloseLine, RiDeleteBinLine, RiDraggable } from '@remixicon/react' import Modal from '@/app/components/base/modal' import Button from '@/app/components/base/button' +import ConfirmAddVar from '@/app/components/app/configuration/config-prompt/confirm-add-var' import type { OpeningStatement } from '@/app/components/base/features/types' +import { getInputKeys } from '@/app/components/base/block-input' +import type { PromptVariable } from '@/models/debug' +import { getNewVar } from '@/utils/var' type OpeningSettingModalProps = { data: OpeningStatement onSave: (newState: OpeningStatement) => void onCancel: () => void + promptVariables?: PromptVariable[] + onAutoAddPromptVariable?: (variable: PromptVariable[]) => void } const MAX_QUESTION_NUM = 5 @@ -19,6 +26,8 @@ const OpeningSettingModal = ({ data, onSave, onCancel, + promptVariables = [], + onAutoAddPromptVariable, }: OpeningSettingModalProps) => { const { t } = useTranslation() const [tempValue, setTempValue] = useState(data?.opening_statement || '') @@ -26,8 +35,29 @@ const OpeningSettingModal = ({ setTempValue(data.opening_statement || '') }, [data.opening_statement]) const [tempSuggestedQuestions, setTempSuggestedQuestions] = useState(data.suggested_questions || []) + const [isShowConfirmAddVar, { setTrue: showConfirmAddVar, setFalse: hideConfirmAddVar }] = useBoolean(false) + const [notIncludeKeys, setNotIncludeKeys] = useState([]) - const handleSave = () => { + const handleSave = useCallback((ignoreVariablesCheck?: boolean) => { + if (!ignoreVariablesCheck) { + const keys = getInputKeys(tempValue) + const promptKeys = promptVariables.map(item => item.key) + let notIncludeKeys: string[] = [] + + if (promptKeys.length === 0) { + if (keys.length > 0) + notIncludeKeys = keys + } + else { + notIncludeKeys = keys.filter(key => !promptKeys.includes(key)) + } + + if (notIncludeKeys.length > 0) { + setNotIncludeKeys(notIncludeKeys) + showConfirmAddVar() + return + } + } const newOpening = produce(data, (draft) => { if (draft) { draft.opening_statement = tempValue @@ -35,7 +65,21 @@ const OpeningSettingModal = ({ } }) onSave(newOpening) - } + }, [data, onSave, promptVariables, showConfirmAddVar, tempSuggestedQuestions, tempValue]) + + const cancelAutoAddVar = useCallback(() => { + hideConfirmAddVar() + handleSave(true) + }, [handleSave, hideConfirmAddVar]) + + const autoAddVar = useCallback(() => { + onAutoAddPromptVariable?.([ + ...promptVariables, + ...notIncludeKeys.map(key => getNewVar(key, 'string')), + ]) + hideConfirmAddVar() + handleSave(true) + }, [handleSave, hideConfirmAddVar, notIncludeKeys, onAutoAddPromptVariable, promptVariables]) const renderQuestions = () => { return ( @@ -137,11 +181,19 @@ const OpeningSettingModal = ({ + {isShowConfirmAddVar && ( + + )} ) } diff --git a/web/app/components/base/features/new-feature-panel/feature-bar.tsx b/web/app/components/base/features/new-feature-panel/feature-bar.tsx index 4fd79f9144..3c2d748975 100644 --- a/web/app/components/base/features/new-feature-panel/feature-bar.tsx +++ b/web/app/components/base/features/new-feature-panel/feature-bar.tsx @@ -118,7 +118,7 @@ const FeatureBar = ({ )} - {/* annotation reply ##TODO## */} + {/* ##TODO## annotation_reply */}
{t('appDebug.feature.bar.enableText')}
+ + + )} + + )} + + + { + setIsShowAnnotationConfigInit(false) + // showChooseFeatureTrue() + }} + onSave={async (embeddingModel, score) => { + await handleEnableAnnotation(embeddingModel, score) + setIsShowAnnotationConfigInit(false) + }} + annotationConfig={annotationReply as any} + /> + {isShowAnnotationFullModal && ( + setIsShowAnnotationFullModal(false)} + /> + )} + + ) +} + +export default AnnotationReply diff --git a/web/app/components/app/configuration/toolbox/score-slider/base-slider/index.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/index.tsx similarity index 100% rename from web/app/components/app/configuration/toolbox/score-slider/base-slider/index.tsx rename to web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/index.tsx diff --git a/web/app/components/app/configuration/toolbox/score-slider/base-slider/style.module.css b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/style.module.css similarity index 100% rename from web/app/components/app/configuration/toolbox/score-slider/base-slider/style.module.css rename to web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider/style.module.css diff --git a/web/app/components/app/configuration/toolbox/score-slider/index.tsx b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx similarity index 90% rename from web/app/components/app/configuration/toolbox/score-slider/index.tsx rename to web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx index 9826cbadcf..d68db9be73 100644 --- a/web/app/components/app/configuration/toolbox/score-slider/index.tsx +++ b/web/app/components/base/features/new-feature-panel/annotation-reply/score-slider/index.tsx @@ -2,7 +2,7 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import Slider from '@/app/components/app/configuration/toolbox/score-slider/base-slider' +import Slider from '@/app/components/base/features/new-feature-panel/annotation-reply/score-slider/base-slider' type Props = { className?: string diff --git a/web/app/components/app/configuration/toolbox/annotation/type.ts b/web/app/components/base/features/new-feature-panel/annotation-reply/type.ts similarity index 100% rename from web/app/components/app/configuration/toolbox/annotation/type.ts rename to web/app/components/base/features/new-feature-panel/annotation-reply/type.ts diff --git a/web/app/components/app/configuration/toolbox/annotation/use-annotation-config.ts b/web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts similarity index 100% rename from web/app/components/app/configuration/toolbox/annotation/use-annotation-config.ts rename to web/app/components/base/features/new-feature-panel/annotation-reply/use-annotation-config.ts diff --git a/web/app/components/base/features/new-feature-panel/feature-bar.tsx b/web/app/components/base/features/new-feature-panel/feature-bar.tsx index 3c2d748975..92273c47ab 100644 --- a/web/app/components/base/features/new-feature-panel/feature-bar.tsx +++ b/web/app/components/base/features/new-feature-panel/feature-bar.tsx @@ -1,7 +1,7 @@ import React, { useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { RiApps2AddLine, RiArrowRightLine, RiSparklingFill } from '@remixicon/react' -import { Citations, ContentModeration, FolderUpload, LoveMessage, Microphone01, TextToAudio, VirtualAssistant } from '@/app/components/base/icons/src/vender/features' +import { Citations, ContentModeration, FolderUpload, LoveMessage, MessageFast, Microphone01, TextToAudio, VirtualAssistant } from '@/app/components/base/icons/src/vender/features' import Button from '@/app/components/base/button' import Tooltip from '@/app/components/base/tooltip' import VoiceSettings from '@/app/components/base/features/new-feature-panel/text-to-speech/voice-settings' @@ -118,7 +118,15 @@ const FeatureBar = ({ )} - {/* ##TODO## annotation_reply */} + {isChatMode && !!features.annotationReply?.enabled && ( + +
+ +
+
+ )}
{t('appDebug.feature.bar.enableText')}
- - - + {!inWorkflow && isChatMode && ( + )} diff --git a/web/app/components/base/features/store.ts b/web/app/components/base/features/store.ts index 7fb3c9848a..9c6e242ab5 100644 --- a/web/app/components/base/features/store.ts +++ b/web/app/components/base/features/store.ts @@ -46,6 +46,7 @@ export const createFeaturesStore = (initProps?: Partial) => { file: { image: { enabled: false, + detail: 'high', number_limits: 3, transfer_methods: [TransferMethod.local_file, TransferMethod.remote_url], }, diff --git a/web/app/components/base/features/types.ts b/web/app/components/base/features/types.ts index 211ba96cd5..0b959c4aa4 100644 --- a/web/app/components/base/features/types.ts +++ b/web/app/components/base/features/types.ts @@ -30,6 +30,7 @@ export type SensitiveWordAvoidance = EnabledOrDisabled & { export type FileUpload = { image?: EnabledOrDisabled & { + detail?: 'high' | 'low' number_limits?: number transfer_methods?: TransferMethod[] } From b863dd7de280ec30b9ed3cadfa6278ed8f40493f Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 4 Sep 2024 10:47:26 +0800 Subject: [PATCH 154/275] fix: list filter init value --- .../workflow/nodes/list-filter/components/filter-condition.tsx | 2 +- web/app/components/workflow/nodes/list-filter/default.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/list-filter/components/filter-condition.tsx b/web/app/components/workflow/nodes/list-filter/components/filter-condition.tsx index 27faf71843..4421b73a12 100644 --- a/web/app/components/workflow/nodes/list-filter/components/filter-condition.tsx +++ b/web/app/components/workflow/nodes/list-filter/components/filter-condition.tsx @@ -22,7 +22,7 @@ type Props = { } const FilterCondition: FC = ({ - condition, + condition = { key: '', comparison_operator: ComparisonOperator.equal, value: '' }, varType, onChange, hasSubVariable, diff --git a/web/app/components/workflow/nodes/list-filter/default.ts b/web/app/components/workflow/nodes/list-filter/default.ts index cd7676fb99..b957a027e5 100644 --- a/web/app/components/workflow/nodes/list-filter/default.ts +++ b/web/app/components/workflow/nodes/list-filter/default.ts @@ -7,6 +7,7 @@ const i18nPrefix = 'workflow.errorMsg' const nodeDefault: NodeDefault = { defaultValue: { variable: [], + filter_by: [], order_by: { enabled: false, key: '', From f6d0fd9848ac28aa42b1f49ff1854fd9353ffd2b Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 4 Sep 2024 11:19:09 +0800 Subject: [PATCH 155/275] feat: add check list filter value --- .../workflow/nodes/list-filter/default.ts | 14 ++++++++++++-- .../workflow/nodes/list-filter/use-config.ts | 14 +++++++++++--- web/i18n/en-US/workflow.ts | 2 ++ web/i18n/zh-Hans/workflow.ts | 2 ++ 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/web/app/components/workflow/nodes/list-filter/default.ts b/web/app/components/workflow/nodes/list-filter/default.ts index b957a027e5..39be24ede5 100644 --- a/web/app/components/workflow/nodes/list-filter/default.ts +++ b/web/app/components/workflow/nodes/list-filter/default.ts @@ -1,5 +1,6 @@ import { BlockEnum } from '../../types' import type { NodeDefault } from '../../types' +import { comparisonOperatorNotRequireValue } from '../if-else/utils' import { type ListFilterNodeType, OrderBy } from './types' import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants' const i18nPrefix = 'workflow.errorMsg' @@ -30,10 +31,19 @@ const nodeDefault: NodeDefault = { }, checkValid(payload: ListFilterNodeType, t: any) { let errorMessages = '' - const { variable } = payload + const { variable, filter_by } = payload if (!errorMessages && !variable?.length) - errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') }) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.inputVar') }) + + // Check filter condition + if (!errorMessages) { + if (!filter_by[0]?.comparison_operator) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.filterConditionComparisonOperator') }) + + if (!errorMessages && !comparisonOperatorNotRequireValue(filter_by[0]?.comparison_operator) && !filter_by[0]?.value) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.filterConditionComparisonValue') }) + } return { isValid: !errorMessages, diff --git a/web/app/components/workflow/nodes/list-filter/use-config.ts b/web/app/components/workflow/nodes/list-filter/use-config.ts index d80fc3d87d..99aa2c45d7 100644 --- a/web/app/components/workflow/nodes/list-filter/use-config.ts +++ b/web/app/components/workflow/nodes/list-filter/use-config.ts @@ -4,7 +4,8 @@ import { useStoreApi } from 'reactflow' import type { ValueSelector, Var } from '../../types' import { VarType } from '../../types' import { getOperators } from '../if-else/utils' -import type { Condition, Limit, ListFilterNodeType, OrderBy } from './types' +import { OrderBy } from './types' +import type { Condition, Limit, ListFilterNodeType } from './types' import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud' import { useIsChatMode, @@ -80,10 +81,12 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { draft.var_type = varType draft.item_var_type = itemVarType draft.filter_by = [{ - key: isFileArray ? 'name' : '', + key: (isFileArray && !draft.filter_by[0].key) ? 'name' : '', comparison_operator: getOperators(itemVarType, isFileArray ? { key: 'name' } : undefined)[0], value: '', }] + if (isFileArray && draft.order_by.enabled && !draft.order_by.key) + draft.order_by.key = 'name' }) setInputs(newInputs) }, [getType, inputs, setInputs]) @@ -110,9 +113,14 @@ const useConfig = (id: string, payload: ListFilterNodeType) => { const handleOrderByEnabledChange = useCallback((enabled: boolean) => { const newInputs = produce(inputs, (draft) => { draft.order_by.enabled = enabled + if (enabled) { + draft.order_by.value = OrderBy.ASC + if (hasSubVariable && !draft.order_by.key) + draft.order_by.key = 'name' + } }) setInputs(newInputs) - }, [inputs, setInputs]) + }, [hasSubVariable, inputs, setInputs]) const handleOrderByKeyChange = useCallback((key: string) => { const newInputs = produce(inputs, (draft) => { diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 3ca5f1fbcb..b89c11aefc 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -562,6 +562,8 @@ const translation = { listFilter: { inputVar: 'Input Variable', filterCondition: 'Filter Condition', + filterConditionComparisonOperator: 'Filter Condition Comparison Operator', + filterConditionComparisonValue: 'Filter Condition value', selectVariableKeyPlaceholder: 'Select sub variable key', limit: 'Limit', orderBy: 'Order by', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 38278d55f2..0307a81090 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -562,6 +562,8 @@ const translation = { listFilter: { inputVar: '输入变量', filterCondition: '过滤条件', + filterConditionComparisonOperator: '过滤条件比较操作符', + filterConditionComparisonValue: '过滤条件比较值', selectVariableKeyPlaceholder: '选择子变量的 Key', limit: '限制', orderBy: '排序', From 97cc9a56159b6fcb5e0046591f63a3ff2c2997a2 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 4 Sep 2024 11:28:36 +0800 Subject: [PATCH 156/275] feat: check file item key not set --- web/app/components/workflow/nodes/list-filter/default.ts | 9 ++++++--- web/i18n/en-US/workflow.ts | 1 + web/i18n/zh-Hans/workflow.ts | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/nodes/list-filter/default.ts b/web/app/components/workflow/nodes/list-filter/default.ts index 39be24ede5..0cd17b6075 100644 --- a/web/app/components/workflow/nodes/list-filter/default.ts +++ b/web/app/components/workflow/nodes/list-filter/default.ts @@ -1,4 +1,4 @@ -import { BlockEnum } from '../../types' +import { BlockEnum, VarType } from '../../types' import type { NodeDefault } from '../../types' import { comparisonOperatorNotRequireValue } from '../if-else/utils' import { type ListFilterNodeType, OrderBy } from './types' @@ -31,14 +31,17 @@ const nodeDefault: NodeDefault = { }, checkValid(payload: ListFilterNodeType, t: any) { let errorMessages = '' - const { variable, filter_by } = payload + const { variable, var_type, filter_by } = payload if (!errorMessages && !variable?.length) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.inputVar') }) // Check filter condition if (!errorMessages) { - if (!filter_by[0]?.comparison_operator) + if (var_type === VarType.arrayFile && !filter_by[0]?.key) + errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.filterConditionKey') }) + + if (!errorMessages && !filter_by[0]?.comparison_operator) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.listFilter.filterConditionComparisonOperator') }) if (!errorMessages && !comparisonOperatorNotRequireValue(filter_by[0]?.comparison_operator) && !filter_by[0]?.value) diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index b89c11aefc..c8f2d400b6 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -562,6 +562,7 @@ const translation = { listFilter: { inputVar: 'Input Variable', filterCondition: 'Filter Condition', + filterConditionKey: 'Filter Condition Key', filterConditionComparisonOperator: 'Filter Condition Comparison Operator', filterConditionComparisonValue: 'Filter Condition value', selectVariableKeyPlaceholder: 'Select sub variable key', diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 0307a81090..eedf0de008 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -562,6 +562,7 @@ const translation = { listFilter: { inputVar: '输入变量', filterCondition: '过滤条件', + filterConditionKey: '过滤条件的 Key', filterConditionComparisonOperator: '过滤条件比较操作符', filterConditionComparisonValue: '过滤条件比较值', selectVariableKeyPlaceholder: '选择子变量的 Key', From 0b94218378a23cc6416361971c8d9989adf63fe6 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 4 Sep 2024 11:43:09 +0800 Subject: [PATCH 157/275] remove unused components --- .../base/feature-panel/index.tsx | 6 - .../config-voice/param-config-content.tsx | 220 ------------------ .../config-voice/param-config.tsx | 41 ---- 3 files changed, 267 deletions(-) delete mode 100644 web/app/components/app/configuration/config-voice/param-config-content.tsx delete mode 100644 web/app/components/app/configuration/config-voice/param-config.tsx diff --git a/web/app/components/app/configuration/base/feature-panel/index.tsx b/web/app/components/app/configuration/base/feature-panel/index.tsx index 1f6db9dee6..badb2d45f4 100644 --- a/web/app/components/app/configuration/base/feature-panel/index.tsx +++ b/web/app/components/app/configuration/base/feature-panel/index.tsx @@ -2,7 +2,6 @@ import type { FC, ReactNode } from 'react' import React from 'react' import cn from '@/utils/classnames' -import ParamsConfig from '@/app/components/app/configuration/config-voice/param-config' export type IFeaturePanelProps = { className?: string @@ -13,7 +12,6 @@ export type IFeaturePanelProps = { isFocus?: boolean noBodySpacing?: boolean children?: ReactNode - isShowTextToSpeech?: boolean } const FeaturePanel: FC = ({ @@ -25,7 +23,6 @@ const FeaturePanel: FC = ({ isFocus, noBodySpacing, children, - isShowTextToSpeech, }) => { return (
= ({
{headerRight &&
{headerRight}
} - {isShowTextToSpeech &&
- -
}
diff --git a/web/app/components/app/configuration/config-voice/param-config-content.tsx b/web/app/components/app/configuration/config-voice/param-config-content.tsx deleted file mode 100644 index 4e70bdda21..0000000000 --- a/web/app/components/app/configuration/config-voice/param-config-content.tsx +++ /dev/null @@ -1,220 +0,0 @@ -'use client' -import useSWR from 'swr' -import type { FC } from 'react' -import { useContext } from 'use-context-selector' -import React, { Fragment } from 'react' -import { usePathname } from 'next/navigation' -import { useTranslation } from 'react-i18next' -import { Listbox, Transition } from '@headlessui/react' -import { CheckIcon, ChevronDownIcon } from '@heroicons/react/20/solid' -import classNames from '@/utils/classnames' -import RadioGroup from '@/app/components/app/configuration/config-vision/radio-group' -import type { Item } from '@/app/components/base/select' -import ConfigContext from '@/context/debug-configuration' -import { fetchAppVoices } from '@/service/apps' -import Tooltip from '@/app/components/base/tooltip' -import { languages } from '@/i18n/language' -import { TtsAutoPlay } from '@/types/app' -const VoiceParamConfig: FC = () => { - const { t } = useTranslation() - const pathname = usePathname() - const matched = pathname.match(/\/app\/([^/]+)/) - const appId = (matched?.length && matched[1]) ? matched[1] : '' - - const { - textToSpeechConfig, - setTextToSpeechConfig, - } = useContext(ConfigContext) - - let languageItem = languages.find(item => item.value === textToSpeechConfig.language) - const localLanguagePlaceholder = languageItem?.name || t('common.placeholder.select') - if (languages && !languageItem && languages.length > 0) - languageItem = languages[0] - const language = languageItem?.value - const voiceItems = useSWR({ appId, language }, fetchAppVoices).data - let voiceItem = voiceItems?.find(item => item.value === textToSpeechConfig.voice) - if (voiceItems && !voiceItem && voiceItems.length > 0) - voiceItem = voiceItems[0] - - const localVoicePlaceholder = voiceItem?.name || t('common.placeholder.select') - - return ( -
-
-
{t('appDebug.voice.voiceSettings.title')}
-
-
-
-
{t('appDebug.voice.voiceSettings.language')}
- - {t('appDebug.voice.voiceSettings.resolutionTooltip').split('\n').map(item => ( -
{item}
- ))} -
- } - /> -
- { - setTextToSpeechConfig({ - ...textToSpeechConfig, - language: String(value.value), - }) - }} - > -
- - - {languageItem?.name ? t(`common.voice.language.${languageItem?.value.replace('-', '')}`) : localLanguagePlaceholder} - - - - - - - - {languages.map((item: Item) => ( - - `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' - }` - } - value={item} - disabled={false} - > - {({ /* active, */ selected }) => ( - <> - {t(`common.voice.language.${(item.value).toString().replace('-', '')}`)} - {(selected || item.value === textToSpeechConfig.language) && ( - - - )} - - )} - - ))} - - -
-
-
-
-
{t('appDebug.voice.voiceSettings.voice')}
- { - if (!value.value) - return - setTextToSpeechConfig({ - ...textToSpeechConfig, - voice: String(value.value), - }) - }} - > -
- - {voiceItem?.name ?? localVoicePlaceholder} - - - - - - - {voiceItems?.map((item: Item) => ( - - `relative cursor-pointer select-none py-2 pl-3 pr-9 rounded-lg hover:bg-gray-100 text-gray-700 ${active ? 'bg-gray-100' : '' - }` - } - value={item} - disabled={false} - > - {({ /* active, */ selected }) => ( - <> - {item.name} - {(selected || item.value === textToSpeechConfig.voice) && ( - - - )} - - )} - - ))} - - -
-
-
-
-
{t('appDebug.voice.voiceSettings.autoPlay')}
- { - setTextToSpeechConfig({ - ...textToSpeechConfig, - autoPlay: value, - }) - }} - /> -
-
-
- - ) -} - -export default React.memo(VoiceParamConfig) diff --git a/web/app/components/app/configuration/config-voice/param-config.tsx b/web/app/components/app/configuration/config-voice/param-config.tsx deleted file mode 100644 index f1e2475495..0000000000 --- a/web/app/components/app/configuration/config-voice/param-config.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client' -import type { FC } from 'react' -import { memo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import VoiceParamConfig from './param-config-content' -import cn from '@/utils/classnames' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' - -const ParamsConfig: FC = () => { - const { t } = useTranslation() - const [open, setOpen] = useState(false) - - return ( - - setOpen(v => !v)}> -
- -
{t('appDebug.voice.settings')}
-
-
- -
- -
-
-
- ) -} -export default memo(ParamsConfig) From b60c7a58262861e2f3f73f7e0e595cd94c9440e1 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 4 Sep 2024 15:45:33 +0800 Subject: [PATCH 158/275] vision config --- .../base/feature-panel/index.tsx | 15 +- .../conversation-histroy/history-panel.tsx | 2 +- .../app/configuration/config-var/index.tsx | 2 +- .../app/configuration/config-vision/index.tsx | 95 ++++++++----- .../config-vision/param-config-content.tsx | 133 ------------------ .../config-vision/param-config.tsx | 41 ------ .../config-vision/radio-group/index.tsx | 40 ------ .../radio-group/style.module.css | 24 ---- .../config/agent/agent-tools/index.tsx | 2 +- .../configuration/dataset-config/index.tsx | 2 +- .../configuration/debug/chat-user-input.tsx | 1 + .../debug-with-multiple-model/chat-item.tsx | 5 +- .../debug/debug-with-multiple-model/index.tsx | 6 +- .../text-generation-item.tsx | 7 +- .../debug/debug-with-single-model/index.tsx | 7 +- .../app/configuration/debug/index.tsx | 49 +++---- .../components/app/configuration/index.tsx | 7 +- .../base/chat/chat/chat-input-area/index.tsx | 4 +- web/app/components/base/chat/chat/index.tsx | 3 + .../annotation-reply/config-param.tsx | 100 ------------- .../new-feature-panel/feature-bar.tsx | 7 +- .../base/features/new-feature-panel/index.tsx | 4 +- 22 files changed, 116 insertions(+), 440 deletions(-) delete mode 100644 web/app/components/app/configuration/config-vision/param-config-content.tsx delete mode 100644 web/app/components/app/configuration/config-vision/param-config.tsx delete mode 100644 web/app/components/app/configuration/config-vision/radio-group/index.tsx delete mode 100644 web/app/components/app/configuration/config-vision/radio-group/style.module.css diff --git a/web/app/components/app/configuration/base/feature-panel/index.tsx b/web/app/components/app/configuration/base/feature-panel/index.tsx index badb2d45f4..9c4adbdd2d 100644 --- a/web/app/components/app/configuration/base/feature-panel/index.tsx +++ b/web/app/components/app/configuration/base/feature-panel/index.tsx @@ -9,7 +9,6 @@ export type IFeaturePanelProps = { title: ReactNode headerRight?: ReactNode hasHeaderBottomBorder?: boolean - isFocus?: boolean noBodySpacing?: boolean children?: ReactNode } @@ -20,25 +19,17 @@ const FeaturePanel: FC = ({ title, headerRight, hasHeaderBottomBorder, - isFocus, noBodySpacing, children, }) => { return ( -
+
{/* Header */} -
+
{headerIcon &&
{headerIcon}
} -
{title}
+
{title}
{headerRight &&
{headerRight}
} diff --git a/web/app/components/app/configuration/config-prompt/conversation-histroy/history-panel.tsx b/web/app/components/app/configuration/config-prompt/conversation-histroy/history-panel.tsx index f40bd4b733..199f9598a4 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-histroy/history-panel.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-histroy/history-panel.tsx @@ -23,7 +23,7 @@ const HistoryPanel: FC = ({ return (
{t('appDebug.feature.conversationHistory.title')}
diff --git a/web/app/components/app/configuration/config-var/index.tsx b/web/app/components/app/configuration/config-var/index.tsx index 802528e0af..5359f2e1e7 100644 --- a/web/app/components/app/configuration/config-var/index.tsx +++ b/web/app/components/app/configuration/config-var/index.tsx @@ -273,7 +273,7 @@ const ConfigVar: FC = ({ promptVariables, readonly, onPromptVar } return ( } diff --git a/web/app/components/app/configuration/config-vision/index.tsx b/web/app/components/app/configuration/config-vision/index.tsx index 515709bff1..ae388615c0 100644 --- a/web/app/components/app/configuration/config-vision/index.tsx +++ b/web/app/components/app/configuration/config-vision/index.tsx @@ -1,61 +1,84 @@ 'use client' import type { FC } from 'react' -import React from 'react' +import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' +import produce from 'immer' import { useContext } from 'use-context-selector' -import Panel from '../base/feature-panel' -import ParamConfig from './param-config' +import { Vision } from '@/app/components/base/icons/src/vender/features' import Tooltip from '@/app/components/base/tooltip' -import Switch from '@/app/components/base/switch' -import { Eye } from '@/app/components/base/icons/src/vender/solid/general' +import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import ConfigContext from '@/context/debug-configuration' +import { Resolution } from '@/types/app' +import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' const ConfigVision: FC = () => { const { t } = useTranslation() - const { - isShowVisionConfig, - visionConfig, - setVisionConfig, - } = useContext(ConfigContext) + const { isShowVisionConfig } = useContext(ConfigContext) + const file = useFeatures(s => s.features.file) + const featuresStore = useFeaturesStore() + + const handleChange = useCallback((resolution: Resolution) => { + const { + features, + setFeatures, + } = featuresStore!.getState() + + const newFeatures = produce(features, (draft) => { + draft.file = { + ...draft.file, + image: { detail: resolution }, + } + }) + setFeatures(newFeatures) + }, [featuresStore]) if (!isShowVisionConfig) return null - return (<> - - } - title={ -
-
{t('appDebug.vision.name')}
+ return ( +
+
+
+ +
+
+
+
{t('appDebug.vision.name')}
+ + {t('appDebug.vision.description')} +
+ } + /> +
+
+
+
{t('appDebug.vision.visionSettings.resolution')}
- {t('appDebug.vision.description')} + {t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => ( +
{item}
+ ))}
} />
- } - headerRight={ -
- -
- setVisionConfig({ - ...visionConfig, - enabled: value, - })} - size='md' +
+ handleChange(Resolution.high)} + /> + handleChange(Resolution.low)} />
- } - noBodySpacing - /> - +
+
) } export default React.memo(ConfigVision) diff --git a/web/app/components/app/configuration/config-vision/param-config-content.tsx b/web/app/components/app/configuration/config-vision/param-config-content.tsx deleted file mode 100644 index cb01f57254..0000000000 --- a/web/app/components/app/configuration/config-vision/param-config-content.tsx +++ /dev/null @@ -1,133 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import { useContext } from 'use-context-selector' -import { useTranslation } from 'react-i18next' -import RadioGroup from './radio-group' -import ConfigContext from '@/context/debug-configuration' -import { Resolution, TransferMethod } from '@/types/app' -import ParamItem from '@/app/components/base/param-item' -import Tooltip from '@/app/components/base/tooltip' - -const MIN = 1 -const MAX = 6 -const ParamConfigContent: FC = () => { - const { t } = useTranslation() - - const { - visionConfig, - setVisionConfig, - } = useContext(ConfigContext) - - const transferMethod = (() => { - if (!visionConfig.transfer_methods || visionConfig.transfer_methods.length === 2) - return TransferMethod.all - - return visionConfig.transfer_methods[0] - })() - - return ( -
-
-
{t('appDebug.vision.visionSettings.title')}
-
-
-
-
{t('appDebug.vision.visionSettings.resolution')}
- - {t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => ( -
{item}
- ))} -
- } - /> -
- { - setVisionConfig({ - ...visionConfig, - detail: value, - }) - }} - /> -
-
-
{t('appDebug.vision.visionSettings.uploadMethod')}
- { - if (value === TransferMethod.all) { - setVisionConfig({ - ...visionConfig, - transfer_methods: [TransferMethod.remote_url, TransferMethod.local_file], - }) - return - } - setVisionConfig({ - ...visionConfig, - transfer_methods: [value], - }) - }} - /> -
-
- { - if (!value) - return - - setVisionConfig({ - ...visionConfig, - number_limits: value, - }) - }} - /> -
-
-
-
- ) -} - -export default React.memo(ParamConfigContent) diff --git a/web/app/components/app/configuration/config-vision/param-config.tsx b/web/app/components/app/configuration/config-vision/param-config.tsx deleted file mode 100644 index f1e2475495..0000000000 --- a/web/app/components/app/configuration/config-vision/param-config.tsx +++ /dev/null @@ -1,41 +0,0 @@ -'use client' -import type { FC } from 'react' -import { memo, useState } from 'react' -import { useTranslation } from 'react-i18next' -import VoiceParamConfig from './param-config-content' -import cn from '@/utils/classnames' -import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' -import { - PortalToFollowElem, - PortalToFollowElemContent, - PortalToFollowElemTrigger, -} from '@/app/components/base/portal-to-follow-elem' - -const ParamsConfig: FC = () => { - const { t } = useTranslation() - const [open, setOpen] = useState(false) - - return ( - - setOpen(v => !v)}> -
- -
{t('appDebug.voice.settings')}
-
-
- -
- -
-
-
- ) -} -export default memo(ParamsConfig) diff --git a/web/app/components/app/configuration/config-vision/radio-group/index.tsx b/web/app/components/app/configuration/config-vision/radio-group/index.tsx deleted file mode 100644 index a1cfb06e6a..0000000000 --- a/web/app/components/app/configuration/config-vision/radio-group/index.tsx +++ /dev/null @@ -1,40 +0,0 @@ -'use client' -import type { FC } from 'react' -import React from 'react' -import s from './style.module.css' -import cn from '@/utils/classnames' - -type OPTION = { - label: string - value: any -} - -type Props = { - className?: string - options: OPTION[] - value: any - onChange: (value: any) => void -} - -const RadioGroup: FC = ({ - className = '', - options, - value, - onChange, -}) => { - return ( -
- {options.map(item => ( -
onChange(item.value)} - > -
-
{item.label}
-
- ))} -
- ) -} -export default React.memo(RadioGroup) diff --git a/web/app/components/app/configuration/config-vision/radio-group/style.module.css b/web/app/components/app/configuration/config-vision/radio-group/style.module.css deleted file mode 100644 index 22c29c6a42..0000000000 --- a/web/app/components/app/configuration/config-vision/radio-group/style.module.css +++ /dev/null @@ -1,24 +0,0 @@ -.item { - @apply grow flex items-center h-8 px-2.5 rounded-lg bg-gray-25 border border-gray-100 cursor-pointer space-x-2; -} - -.item:hover { - background-color: #ffffff; - border-color: #B2CCFF; - box-shadow: 0px 12px 16px -4px rgba(16, 24, 40, 0.08), 0px 4px 6px -2px rgba(16, 24, 40, 0.03); -} - -.item.checked { - background-color: #ffffff; - border-color: #528BFF; - box-shadow: 0px 1px 2px 0px rgba(16, 24, 40, 0.06), 0px 1px 3px 0px rgba(16, 24, 40, 0.10); -} - -.radio { - @apply w-4 h-4 border-[2px] border-gray-200 rounded-full; -} - -.item.checked .radio { - border-width: 5px; - border-color: #155eef; -} \ No newline at end of file diff --git a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx index 1280fd8928..52e5d5d906 100644 --- a/web/app/components/app/configuration/config/agent/agent-tools/index.tsx +++ b/web/app/components/app/configuration/config/agent/agent-tools/index.tsx @@ -58,7 +58,7 @@ const AgentTools: FC = () => { return ( <> diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index aa92f03baa..c98e90b18e 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -70,7 +70,7 @@ const DatasetConfig: FC = () => { return (
+ {/* ##TODO## file_upload */} {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
= ({ modelConfig, appId, inputs, - visionConfig, collectionList, } = useDebugConfigurationContext() const { textGenerationModelList } = useProviderContext() @@ -99,7 +98,7 @@ const ChatItem: FC = ({ model_config: configData, } - if (visionConfig.enabled && files?.length && supportVision) + if ((config.file_upload as any).enabled && files?.length && supportVision) data.files = files handleSend( @@ -110,7 +109,7 @@ const ChatItem: FC = ({ onGetSuggestedQuestions: (responseItemId, getAbortController) => fetchSuggestedQuestions(appId, responseItemId, getAbortController), }, ) - }, [appId, config, handleSend, inputs, modelAndParameter, textGenerationModelList, visionConfig.enabled]) + }, [appId, config, handleSend, inputs, modelAndParameter, textGenerationModelList]) const { eventEmitter } = useEventEmitterContextContext() eventEmitter?.useSubscription((v: any) => { diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx index 30356b2b1b..209485042b 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx @@ -21,9 +21,10 @@ import { useStore as useAppStore } from '@/app/components/app/store' const DebugWithMultipleModel = () => { const { mode, - visionConfig, + isShowVisionConfig, } = useDebugConfigurationContext() const speech2text = useFeatures(s => s.features.speech2text) + const file = useFeatures(s => s.features.file) const { multipleModelConfigs, checkCanSend, @@ -129,10 +130,11 @@ const DebugWithMultipleModel = () => {
)} diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx index 289504f72c..57c8f83f3f 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/text-generation-item.tsx @@ -36,7 +36,6 @@ const TextGenerationItem: FC = ({ completionPromptConfig, dataSets, datasetConfigs, - visionConfig, } = useDebugConfigurationContext() const { textGenerationModelList } = useProviderContext() const features = useFeatures(s => s.features) @@ -58,10 +57,8 @@ const TextGenerationItem: FC = ({ more_like_this: features.moreLikeThis as any, sensitive_word_avoidance: features.moderation as any, text_to_speech: features.text2speech as any, + file_upload: features.file as any, opening_statement: introduction, - file_upload: { - image: visionConfig, - }, speech_to_text: speechToTextConfig, suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig, retriever_resource: citationConfig, @@ -103,7 +100,7 @@ const TextGenerationItem: FC = ({ model_config: configData, } - if (visionConfig.enabled && files && files?.length > 0) { + if ((config.file_upload as any).enabled && files && files?.length > 0) { data.files = files.map((item) => { if (item.transfer_method === TransferMethod.local_file) { return { diff --git a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx index b3fef73f5b..67a9a419a3 100644 --- a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx +++ b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx @@ -39,9 +39,9 @@ const DebugWithSingleModel = forwardRef s.features) @@ -105,7 +105,7 @@ const DebugWithSingleModel = forwardRef fetchSuggestedQuestions(appId, responseItemId, getAbortController), }, ) - }, [appId, checkCanSend, completionParams, config, handleSend, inputs, modelConfig, textGenerationModelList, visionConfig.enabled]) + }, [appId, checkCanSend, completionParams, config, handleSend, inputs, modelConfig, textGenerationModelList]) const allToolIcons = useMemo(() => { const icons: Record = {} @@ -142,6 +142,7 @@ const DebugWithSingleModel = forwardRef = ({ speechToTextConfig, textToSpeechConfig, citationConfig, - // moderationConfig, - // moreLikeThisConfig, formattingChanged, setFormattingChanged, dataSets, @@ -93,8 +91,6 @@ const Debug: FC = ({ completionParams, hasSetContextVar, datasetConfigs, - visionConfig, - setVisionConfig, } = useContext(ConfigContext) const { eventEmitter } = useEventEmitterContextContext() const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.textEmbedding) @@ -203,6 +199,7 @@ const Debug: FC = ({ const [completionRes, setCompletionRes] = useState('') const [messageId, setMessageId] = useState(null) const features = useFeatures(s => s.features) + const featuresStore = useFeaturesStore() const sendTextCompletion = async () => { if (isResponding) { @@ -252,10 +249,7 @@ const Debug: FC = ({ more_like_this: features.moreLikeThis as any, sensitive_word_avoidance: features.moderation as any, text_to_speech: features.text2speech as any, - // ##TODO## file_upload - file_upload: { - image: visionConfig, - }, + file_upload: features.file as any, opening_statement: introduction, suggested_questions_after_answer: suggestedQuestionsAfterAnswerConfig, speech_to_text: speechToTextConfig, @@ -272,7 +266,7 @@ const Debug: FC = ({ model_config: postModelConfig, } - if (visionConfig.enabled && completionFiles && completionFiles?.length > 0) { + if ((features.file as any).enabled && completionFiles && completionFiles?.length > 0) { data.files = completionFiles.map((item) => { if (item.transfer_method === TransferMethod.local_file) { return { @@ -348,7 +342,7 @@ const Debug: FC = ({ ) } - const handleVisionConfigInMultipleModel = () => { + const handleVisionConfigInMultipleModel = useCallback(() => { if (debugWithMultipleModel && mode) { const supportedVision = multipleModelConfigs.some((modelConfig) => { const currentProvider = textGenerationModelList.find(modelItem => modelItem.provider === modelConfig.provider) @@ -356,25 +350,24 @@ const Debug: FC = ({ return currentModel?.features?.includes(ModelFeatureEnum.vision) }) + const { + features, + setFeatures, + } = featuresStore!.getState() - if (supportedVision) { - setVisionConfig({ - ...visionConfig, - enabled: true, - }, true) - } - else { - setVisionConfig({ - ...visionConfig, - enabled: false, - }, true) - } + const newFeatures = produce(features, (draft) => { + draft.file = { + ...draft.file, + enabled: supportedVision, + } + }) + setFeatures(newFeatures) } - } + }, [debugWithMultipleModel, featuresStore, mode, multipleModelConfigs, textGenerationModelList]) useEffect(() => { handleVisionConfigInMultipleModel() - }, [multipleModelConfigs, mode]) + }, [multipleModelConfigs, mode, handleVisionConfigInMultipleModel]) const { currentLogItem, setCurrentLogItem, showPromptLogModal, setShowPromptLogModal, showAgentLogModal, setShowAgentLogModal } = useAppStore(useShallow(state => ({ currentLogItem: state.currentLogItem, @@ -455,7 +448,7 @@ const Debug: FC = ({ onSend={handleSendTextCompletion} inputs={inputs} visionConfig={{ - ...visionConfig, + ...features.file! as VisionSettings, image_file_size_limit: fileUploadConfigResponse?.image_file_size_limit, }} onVisionFilesChange={setCompletionFiles} diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 94f8c01985..1ba1d759bd 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -598,7 +598,6 @@ const Configuration: FC = () => { completionParams: model.completion_params, } - // ##TODO## new vision config if (modelConfig.file_upload) handleSetVisionConfig(modelConfig.file_upload.image, true) @@ -693,10 +692,7 @@ const Configuration: FC = () => { sensitive_word_avoidance: features?.moderation as any, speech_to_text: features?.speech2text as any, text_to_speech: features?.text2speech as any, - // ##TODO## file_upload - file_upload: { - image: visionConfig, - }, + file_upload: features?.file as any, suggested_questions_after_answer: features?.suggested as any, retriever_resource: features?.citation as any, agent_mode: { @@ -983,6 +979,7 @@ const Configuration: FC = () => { void visionConfig?: FileUpload @@ -33,6 +34,7 @@ type ChatInputAreaProps = { } const ChatInputArea = ({ showFeatureBar, + showFileUpload, featureBarDisabled, onFeatureBarClick, visionConfig, @@ -155,7 +157,7 @@ const ChatInputArea = ({ ) }
- {showFeatureBar && } + {showFeatureBar && } ) diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index 601b7ec7dd..cda20b0209 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/web/app/components/base/chat/chat/index.tsx @@ -62,6 +62,7 @@ export type ChatProps = { hideLogModal?: boolean themeBuilder?: ThemeBuilder showFeatureBar?: boolean + showFileUpload?: boolean onFeatureBarClick?: (state: boolean) => void } @@ -92,6 +93,7 @@ const Chat: FC = ({ hideLogModal, themeBuilder, showFeatureBar, + showFileUpload, onFeatureBarClick, }) => { const { t } = useTranslation() @@ -267,6 +269,7 @@ const Chat: FC = ({ !noChatInput && ( void - onScoreChange: (score: number, embeddingModel?: EmbeddingModelConfig) => void -} export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }> = ({ title, @@ -38,87 +22,3 @@ export const Item: FC<{ title: string; tooltip: string; children: JSX.Element }>
) } - -const AnnotationReplyConfig: FC = ({ - onEmbeddingChange, - onScoreChange, -}) => { - const { t } = useTranslation() - const router = useRouter() - const pathname = usePathname() - const matched = pathname.match(/\/app\/([^/]+)/) - const appId = (matched?.length && matched[1]) ? matched[1] : '' - const { - annotationConfig, - } = useContext(ConfigContext) - - const [isShowEdit, setIsShowEdit] = React.useState(false) - - return ( - <> - - } - title={t('appDebug.feature.annotation.title')} - headerRight={ -
-
{ setIsShowEdit(true) }} - > - -
- - {t('common.operation.params')} -
-
-
{ - router.push(`/app/${appId}/annotations`) - }}> -
{t('appDebug.feature.annotation.cacheManagement')}
- -
-
- } - noBodySpacing - /> - {isShowEdit && ( - { - setIsShowEdit(false) - }} - onSave={async (embeddingModel, score) => { - const annotationConfig = await fetchAnnotationConfig(appId) as AnnotationReplyConfigType - let isEmbeddingModelChanged = false - if ( - embeddingModel.embedding_model_name !== annotationConfig.embedding_model.embedding_model_name - && embeddingModel.embedding_provider_name !== annotationConfig.embedding_model.embedding_provider_name - ) { - await onEmbeddingChange(embeddingModel) - isEmbeddingModelChanged = true - } - - if (score !== annotationConfig.score_threshold) { - await updateAnnotationScore(appId, annotationConfig.id, score) - if (isEmbeddingModelChanged) - onScoreChange(score, embeddingModel) - - else - onScoreChange(score) - } - - setIsShowEdit(false) - }} - annotationConfig={annotationConfig} - /> - )} - - ) -} -export default React.memo(AnnotationReplyConfig) diff --git a/web/app/components/base/features/new-feature-panel/feature-bar.tsx b/web/app/components/base/features/new-feature-panel/feature-bar.tsx index 92273c47ab..42fe5555c6 100644 --- a/web/app/components/base/features/new-feature-panel/feature-bar.tsx +++ b/web/app/components/base/features/new-feature-panel/feature-bar.tsx @@ -10,12 +10,14 @@ import cn from '@/utils/classnames' type Props = { isChatMode?: boolean + showFileUpload?: boolean disabled?: boolean onFeatureBarClick?: (state: boolean) => void } const FeatureBar = ({ isChatMode = true, + showFileUpload = true, disabled, onFeatureBarClick, }: Props) => { @@ -28,9 +30,10 @@ const FeatureBar = ({ const data = { ...features, citation: { enabled: isChatMode ? features.citation?.enabled : false }, + file: showFileUpload ? features.file! : { enabled: false }, } return !Object.values(data).some(f => f.enabled) - }, [features, isChatMode]) + }, [features, isChatMode, showFileUpload]) return (
@@ -91,7 +94,7 @@ const FeatureBar = ({ )} - {!!features.file?.enabled && ( + {showFileUpload && !!features.file?.enabled && ( diff --git a/web/app/components/base/features/new-feature-panel/index.tsx b/web/app/components/base/features/new-feature-panel/index.tsx index a1b95e6d20..517364e7ae 100644 --- a/web/app/components/base/features/new-feature-panel/index.tsx +++ b/web/app/components/base/features/new-feature-panel/index.tsx @@ -24,6 +24,7 @@ type Props = { onChange?: OnFeaturesChange onClose: () => void inWorkflow?: boolean + showFileUpload?: boolean promptVariables?: PromptVariable[] onAutoAddPromptVariable?: (variable: PromptVariable[]) => void } @@ -35,6 +36,7 @@ const NewFeaturePanel = ({ onChange, onClose, inWorkflow = true, + showFileUpload = true, promptVariables, onAutoAddPromptVariable, }: Props) => { @@ -77,7 +79,7 @@ const NewFeaturePanel = ({ {text2speechDefaultModel && ( )} - + {showFileUpload && } {isChatMode && ( )} From d933ebb845c7bd4cff63291e57171bbc70b7322a Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Mon, 9 Sep 2024 11:27:35 +0800 Subject: [PATCH 159/275] file input --- .../base/file-uploader/file-from-link-or-local/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/web/app/components/base/file-uploader/file-from-link-or-local/index.tsx b/web/app/components/base/file-uploader/file-from-link-or-local/index.tsx index 159b037c84..0854787392 100644 --- a/web/app/components/base/file-uploader/file-from-link-or-local/index.tsx +++ b/web/app/components/base/file-uploader/file-from-link-or-local/index.tsx @@ -72,11 +72,17 @@ const FileFromLinkOrLocal = ({ { showFromLocal && ( ) } From 6c9c3faf788e6b66e0e046ec42da1035be6f6c6e Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 9 Sep 2024 14:36:56 +0800 Subject: [PATCH 160/275] fix: allow file extensions remove . --- .../nodes/_base/components/file-upload-setting.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx index e791afbb66..d915f59036 100644 --- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -68,9 +68,9 @@ const FileUploadSetting: FC = ({ const handleCustomFileTypesChange = useCallback((customFileTypes: string[]) => { const newPayload = produce(payload, (draft) => { draft.allowed_file_extensions = customFileTypes.map((v) => { - if (v.startsWith('.')) - return v - return `.${v}` + if (v.startsWith('.')) // Not start with dot + return v.slice(1) + return v }) }) onChange(newPayload) @@ -104,7 +104,7 @@ const FileUploadSetting: FC = ({ type={SupportUploadFileTypes.custom} selected={allowed_file_types.includes(SupportUploadFileTypes.custom)} onToggle={handleSupportFileTypeChange} - customFileTypes={allowed_file_extensions} + customFileTypes={allowed_file_extensions?.map(item => `.${item}`)} onCustomFileTypesChange={handleCustomFileTypesChange} />
From c159b7a78152a4ccf2f8f6618bfc343d5886662e Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 9 Sep 2024 15:15:08 +0800 Subject: [PATCH 161/275] chore: transform field type css to tailwind and multi theme --- .../config-var/select-type-item/index.tsx | 7 ++-- .../select-type-item/style.module.css | 40 ------------------- 2 files changed, 4 insertions(+), 43 deletions(-) delete mode 100644 web/app/components/app/configuration/config-var/select-type-item/style.module.css diff --git a/web/app/components/app/configuration/config-var/select-type-item/index.tsx b/web/app/components/app/configuration/config-var/select-type-item/index.tsx index e747e86619..f96e80cb78 100644 --- a/web/app/components/app/configuration/config-var/select-type-item/index.tsx +++ b/web/app/components/app/configuration/config-var/select-type-item/index.tsx @@ -2,7 +2,6 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import s from './style.module.css' import cn from '@/utils/classnames' import type { InputVarType } from '@/app/components/workflow/types' import InputVarTypeIcon from '@/app/components/workflow/nodes/_base/components/input-var-type-icon' @@ -27,13 +26,15 @@ const SelectTypeItem: FC = ({ return (
- {typeName} + {typeName}
) } diff --git a/web/app/components/app/configuration/config-var/select-type-item/style.module.css b/web/app/components/app/configuration/config-var/select-type-item/style.module.css deleted file mode 100644 index 5007ed7fed..0000000000 --- a/web/app/components/app/configuration/config-var/select-type-item/style.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.item { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - height: 58px; - /* width: 98px; */ - border-radius: 8px; - border: 1px solid #EAECF0; - box-shadow: 0px 1px 2px rgba(16, 24, 40, 0.05); - background-color: #fff; - cursor: pointer; -} - -.item:not(.selected):hover { - border-color: #B2CCFF; - background-color: #F5F8FF; - box-shadow: 0px 4px 8px -2px rgba(16, 24, 40, 0.1), 0px 2px 4px -2px rgba(16, 24, 40, 0.06); -} - -.item.selected { - color: #155EEF; - border-color: #528BFF; - background-color: #F5F8FF; - box-shadow: 0px 1px 3px rgba(16, 24, 40, 0.1), 0px 1px 2px rgba(16, 24, 40, 0.06); -} - -.text { - font-size: 13px; - color: #667085; - font-weight: 500; -} - -.item.selected .text { - color: #155EEF; -} - -.item:not(.selected):hover { - color: #344054; -} \ No newline at end of file From 007a6fd14abf203f3406388b2b62cb634c34940c Mon Sep 17 00:00:00 2001 From: Joel Date: Mon, 9 Sep 2024 15:24:50 +0800 Subject: [PATCH 162/275] chore: other file types placeholder add + --- web/i18n/en-US/app-debug.ts | 2 +- web/i18n/zh-Hans/app-debug.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/i18n/en-US/app-debug.ts b/web/i18n/en-US/app-debug.ts index e8569324be..ffae42a5c9 100644 --- a/web/i18n/en-US/app-debug.ts +++ b/web/i18n/en-US/app-debug.ts @@ -354,7 +354,7 @@ const translation = { custom: { name: 'Other file types', description: 'Specify other file types.', - createPlaceholder: 'File extension, e.g .doc', + createPlaceholder: '+ File extension, e.g .doc', }, }, 'maxNumberOfUploads': 'Max number of uploads', diff --git a/web/i18n/zh-Hans/app-debug.ts b/web/i18n/zh-Hans/app-debug.ts index 1bc27ef549..a64150befa 100644 --- a/web/i18n/zh-Hans/app-debug.ts +++ b/web/i18n/zh-Hans/app-debug.ts @@ -350,7 +350,7 @@ const translation = { custom: { name: '其他文件类型', description: '指定其他文件类型', - createPlaceholder: '文件扩展名,例如 .doc', + createPlaceholder: '+ 文件扩展名,例如 .doc', }, }, 'content': '内容', From 264f7c21396886ed2ccc27470782aaed94fd8b4e Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 10:59:13 +0800 Subject: [PATCH 163/275] fix: file show type error --- .../workflow/nodes/_base/components/variable/utils.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index eaa35d9951..5a7e7886fa 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -52,18 +52,18 @@ const inputVarTypeToVarType = (type: InputVarType): VarType => { } as any)[type] || VarType.string } -const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: ValueSelector) => boolean, value_selector: ValueSelector): Var => { +const findExceptVarInObject = (obj: any, filterVar: (payload: Var, selector: ValueSelector) => boolean, value_selector: ValueSelector, isFile?: boolean): Var => { const { children } = obj const res: Var = { variable: obj.variable, - type: VarType.object, + type: isFile ? VarType.file : VarType.object, children: children.filter((item: Var) => { const { children } = item const currSelector = [...value_selector, item.variable] if (!children) return filterVar(item, currSelector) - const obj = findExceptVarInObject(item, filterVar, currSelector) + const obj = findExceptVarInObject(item, filterVar, currSelector, false) // File doesn't contains file children return obj.children && obj.children?.length > 0 }), } @@ -302,7 +302,6 @@ const formatItem = ( } const selector = [id] - res.vars = res.vars.filter((v) => { const isCurrentMatched = filterVar(v, (() => { const variableArr = v.variable.split('.') @@ -330,7 +329,7 @@ const formatItem = ( if (!children) return false - const obj = findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector) + const obj = findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector, isFile) return obj?.children && obj?.children.length > 0 }).map((v) => { const isFile = v.type === VarType.file @@ -352,7 +351,7 @@ const formatItem = ( if (!children) return v - return findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector) + return findExceptVarInObject(isFile ? { ...v, children } : v, filterVar, selector, isFile) }) return res From 97056dad308c361f159b55f14964afc2df7d93a2 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 11:54:34 +0800 Subject: [PATCH 164/275] fix: file type var match page crash --- .../workflow/nodes/_base/components/variable/utils.ts | 4 ++-- .../components/workflow/nodes/_base/hooks/use-one-step-run.ts | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/utils.ts b/web/app/components/workflow/nodes/_base/components/variable/utils.ts index 5a7e7886fa..7a1fbdb7f8 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/variable/utils.ts @@ -421,7 +421,7 @@ const getIterationItemType = ({ arrayType = curr?.type } else { - if (curr?.type === VarType.object) + if (curr?.type === VarType.object || curr?.type === VarType.file) curr = curr.children } }) @@ -516,7 +516,7 @@ export const getVarType = ({ type = curr?.type } else { - if (curr?.type === VarType.object) + if (curr?.type === VarType.object || curr?.type === VarType.file) curr = curr.children } }) diff --git a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts index 0a6a7a9c1b..8876ab0058 100644 --- a/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts +++ b/web/app/components/workflow/nodes/_base/hooks/use-one-step-run.ts @@ -123,7 +123,7 @@ const useOneStepRun = ({ res = curr } else { - if (curr?.type === VarType.object) + if (curr?.type === VarType.object || curr?.type === VarType.file) curr = curr.children } }) From 32b6c7063a45eb26ade6b006a704f00704f7a36b Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Tue, 10 Sep 2024 14:17:29 +0800 Subject: [PATCH 165/275] file uploader --- .../chat/chat/chat-input-area/operation.tsx | 2 +- .../file-from-link-or-local/index.tsx | 14 +++++++- .../file-list-flex-operation.tsx | 35 ++++++++++++------- .../components/base/file-uploader/hooks.ts | 33 +++++++++-------- .../components/base/file-uploader/store.tsx | 6 ++-- .../components/base/file-uploader/types.ts | 6 +++- .../components/base/file-uploader/utils.ts | 6 ++++ 7 files changed, 69 insertions(+), 33 deletions(-) diff --git a/web/app/components/base/chat/chat/chat-input-area/operation.tsx b/web/app/components/base/chat/chat/chat-input-area/operation.tsx index d1524f29ce..c31ef19abe 100644 --- a/web/app/components/base/chat/chat/chat-input-area/operation.tsx +++ b/web/app/components/base/chat/chat/chat-input-area/operation.tsx @@ -38,7 +38,7 @@ const Operation = forwardRef(({ ref={ref} >
- {visionConfig?.enabled && } + { speechToTextConfig?.enabled && ( ) => { + const file = e.target.files?.[0] + + if (!file) + return + + handleLocalFileUpload(file) + } return ( ((e.target as HTMLInputElement).value = '')} type='file' - onChange={() => {}} + onChange={handleChange} /> ) diff --git a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx index 9412a96ac8..583a4ff556 100644 --- a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx +++ b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx @@ -4,12 +4,14 @@ import { } from 'react' import { RiCloseLine } from '@remixicon/react' import { useStore } from '../store' +import { useFile } from '../hooks' import FileListItem from './file-list-flex-item' import Button from '@/app/components/base/button' import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' const FileListFlexOperation = forwardRef((_, ref) => { const files = useStore(s => s.files) + const { handleRemoveFile } = useFile() return (
((_, ref) => { key={file._id} className='relative' > - -
- -
+ { + file._progress !== 100 && ( +
+ +
+ ) + }
)) diff --git a/web/app/components/base/file-uploader/hooks.ts b/web/app/components/base/file-uploader/hooks.ts index 2c02b53640..fdae208e4e 100644 --- a/web/app/components/base/file-uploader/hooks.ts +++ b/web/app/components/base/file-uploader/hooks.ts @@ -6,7 +6,7 @@ import { import produce from 'immer' import { v4 as uuid4 } from 'uuid' import { useTranslation } from 'react-i18next' -import type { TFile } from './types' +import type { FileEntity } from './types' import { useFileStore } from './store' import { fileUpload } from './utils' import { useToastContext } from '@/app/components/base/toast' @@ -15,15 +15,12 @@ type UseFileParams = { isPublicAPI?: boolean url?: string } -export const useFile = ({ - isPublicAPI, - url, -}: UseFileParams) => { +export const useFile = () => { const { t } = useTranslation() const { notify } = useToastContext() const fileStore = useFileStore() - const handleAddOrUpdateFiles = useCallback((newFile: TFile) => { + const handleAddOrUpdateFiles = useCallback((newFile: FileEntity) => { const { files, setFiles, @@ -71,29 +68,37 @@ export const useFile = ({ setFiles([]) }, [fileStore]) - const handleLocalFileUpload = useCallback((file: File) => { + const handleLocalFileUpload = useCallback(( + file: File, + { + isPublicAPI, + url, + }: UseFileParams = { isPublicAPI: false }, + ) => { const reader = new FileReader() + const isImage = file.type.startsWith('image') reader.addEventListener( 'load', () => { - const imageFile = { + const uploadingFile = { _id: uuid4(), file, _url: reader.result as string, _progress: 0, + _base64Url: isImage ? reader.result as string : '', } - handleAddOrUpdateFiles(imageFile) + handleAddOrUpdateFiles(uploadingFile) fileUpload({ - file: imageFile.file, + file: uploadingFile.file, onProgressCallback: (progress) => { - handleAddOrUpdateFiles({ ...imageFile, _progress: progress }) + handleAddOrUpdateFiles({ ...uploadingFile, _progress: progress }) }, onSuccessCallback: (res) => { - handleAddOrUpdateFiles({ ...imageFile, _fileId: res.id, _progress: 100 }) + handleAddOrUpdateFiles({ ...uploadingFile, _fileId: res.id, _progress: 100 }) }, onErrorCallback: () => { notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') }) - handleAddOrUpdateFiles({ ...imageFile, _progress: -1 }) + handleAddOrUpdateFiles({ ...uploadingFile, _progress: -1 }) }, }, isPublicAPI, url) }, @@ -107,7 +112,7 @@ export const useFile = ({ false, ) reader.readAsDataURL(file) - }, [notify, t, handleAddOrUpdateFiles, isPublicAPI, url]) + }, [notify, t, handleAddOrUpdateFiles]) const handleClipboardPasteFile = useCallback((e: ClipboardEvent) => { const file = e.clipboardData?.files[0] diff --git a/web/app/components/base/file-uploader/store.tsx b/web/app/components/base/file-uploader/store.tsx index ba0b63bc27..1d58de145e 100644 --- a/web/app/components/base/file-uploader/store.tsx +++ b/web/app/components/base/file-uploader/store.tsx @@ -7,11 +7,11 @@ import { useStore as useZustandStore, } from 'zustand' import { createStore } from 'zustand/vanilla' -import type { TFile } from './types' +import type { FileEntity } from './types' type Shape = { - files: TFile[] - setFiles: (files: TFile[]) => void + files: FileEntity[] + setFiles: (files: FileEntity[]) => void } export const createFileStore = () => { diff --git a/web/app/components/base/file-uploader/types.ts b/web/app/components/base/file-uploader/types.ts index 3298d7e7a4..a28152a4f8 100644 --- a/web/app/components/base/file-uploader/types.ts +++ b/web/app/components/base/file-uploader/types.ts @@ -1,3 +1,5 @@ +import type { TransferMethod } from '@/types/app' + export enum FileTypeEnum { IMAGE = 'IMAGE', VIDEO = 'VIDEO', @@ -13,10 +15,12 @@ export enum FileTypeEnum { OTHER = 'OTHER', } -export type TFile = { +export type FileEntity = { file: File _id: string _fileId?: string _progress?: number _url?: string + _base64Url?: string + _method?: TransferMethod } diff --git a/web/app/components/base/file-uploader/utils.ts b/web/app/components/base/file-uploader/utils.ts index 41715bdc74..66e6668f76 100644 --- a/web/app/components/base/file-uploader/utils.ts +++ b/web/app/components/base/file-uploader/utils.ts @@ -34,3 +34,9 @@ export const fileUpload: FileUpload = ({ onErrorCallback() }) } + +export const isFileType = (type: string) => { + return (file: File) => { + return file.type === type + } +} From a689cd6fd4008b1a4712e6c4b1076e31b538b629 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 14:47:24 +0800 Subject: [PATCH 166/275] chore: var refernece support theme --- .../components/variable/var-reference-vars.tsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index fdf1037c6d..2f2371ce52 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -97,28 +97,28 @@ const Item: FC = ({ ref={itemRef} className={cn( isObj ? ' pr-1' : 'pr-[18px]', - isHovering && (isObj ? 'bg-primary-50' : 'bg-gray-50'), + isHovering && (isObj ? 'bg-primary-50' : 'bg-state-base-hover'), 'relative w-full flex items-center h-6 pl-3 rounded-md cursor-pointer') } onClick={handleChosen} >
- {!isEnv && !isChatVar && } + {!isEnv && !isChatVar && } {isEnv && } {isChatVar && } {!isEnv && !isChatVar && ( -
{itemData.variable}
+
{itemData.variable}
)} {isEnv && ( -
{itemData.variable.replace('env.', '')}
+
{itemData.variable.replace('env.', '')}
)} {isChatVar && ( -
{itemData.variable.replace('conversation.', '')}
+
{itemData.variable.replace('conversation.', '')}
)}
-
{itemData.type}
+
{itemData.type}
{isObj && ( - + )}
@@ -285,7 +285,7 @@ const VarReferenceVars: FC = ({ filteredVars.map((item, i) => (
{item.title}
{item.vars.map((v, j) => ( From ea40b1dcb2110653acbdc9ed28f0bd9de9427317 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 15:05:54 +0800 Subject: [PATCH 167/275] fix: two scrollbar --- .../prompt-editor/plugins/component-picker-block/index.tsx | 4 ++-- .../nodes/_base/components/variable/var-reference-vars.tsx | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx index 15b07ded17..0cadf9f95f 100644 --- a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx @@ -133,11 +133,10 @@ const ComponentPicker = ({ // See https://github.com/facebook/lexical/blob/ac97dfa9e14a73ea2d6934ff566282d7f758e8bb/packages/lexical-react/src/shared/LexicalMenu.ts#L493
@@ -178,6 +177,7 @@ const ComponentPicker = ({ onChange={(variables: string[]) => { handleSelectWorkflowVariable(variables) }} + maxHeightClass='max-h-[34vh]' />
diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 2f2371ce52..ab71c959d5 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -219,6 +219,7 @@ type Props = { vars: NodeOutPutVar[] onChange: (value: ValueSelector, item: Var) => void itemWidth?: number + maxHeightClass?: string } const VarReferenceVars: FC = ({ hideSearch, @@ -226,6 +227,7 @@ const VarReferenceVars: FC = ({ vars, onChange, itemWidth, + maxHeightClass, }) => { const { t } = useTranslation() const [searchText, setSearchText] = useState('') @@ -279,7 +281,7 @@ const VarReferenceVars: FC = ({ } {filteredVars.length > 0 - ?
+ ?
{ filteredVars.map((item, i) => ( From aa8499efac27ef470e071fe43a1a77fae128849a Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 15:16:27 +0800 Subject: [PATCH 168/275] fix: doc extract var type --- web/app/components/workflow/nodes/doc-extractor/use-config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/workflow/nodes/doc-extractor/use-config.ts b/web/app/components/workflow/nodes/doc-extractor/use-config.ts index 45ad397003..2bb2897b96 100644 --- a/web/app/components/workflow/nodes/doc-extractor/use-config.ts +++ b/web/app/components/workflow/nodes/doc-extractor/use-config.ts @@ -21,7 +21,7 @@ const useConfig = (id: string, payload: DocExtractorNodeType) => { }, [inputs, setInputs]) const filterVar = useCallback((varPayload: Var) => { - return varPayload.type !== VarType.file + return varPayload.type === VarType.file }, []) return { From f0e81e3918b2b2f49db88092cf3fed3f70d544e1 Mon Sep 17 00:00:00 2001 From: Joel Date: Tue, 10 Sep 2024 15:22:44 +0800 Subject: [PATCH 169/275] fix: doc extract and node isconversation item --- web/app/components/workflow/nodes/doc-extractor/node.tsx | 8 +++++--- web/app/components/workflow/nodes/list-filter/node.tsx | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/web/app/components/workflow/nodes/doc-extractor/node.tsx b/web/app/components/workflow/nodes/doc-extractor/node.tsx index 8558ca81eb..655162acab 100644 --- a/web/app/components/workflow/nodes/doc-extractor/node.tsx +++ b/web/app/components/workflow/nodes/doc-extractor/node.tsx @@ -4,10 +4,10 @@ import { useNodes } from 'reactflow' import { useTranslation } from 'react-i18next' import NodeVariableItem from '../variable-assigner/components/node-variable-item' import { type DocExtractorNodeType } from './types' -import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' -const i18nPrefix = 'workflow.nodes.assigner' +const i18nPrefix = 'workflow.nodes.docExtractor' const NodeComponent: FC> = ({ data, @@ -22,14 +22,16 @@ const NodeComponent: FC> = ({ const isSystem = isSystemVar(variable) const isEnv = isENV(variable) + const isChatVar = isConversationVar(variable) const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.') return (
-
{t(`${i18nPrefix}.assignedVariable`)}
+
{t(`${i18nPrefix}.inputVar`)}
diff --git a/web/app/components/workflow/nodes/list-filter/node.tsx b/web/app/components/workflow/nodes/list-filter/node.tsx index 2bfde0f9b4..721e13b2d2 100644 --- a/web/app/components/workflow/nodes/list-filter/node.tsx +++ b/web/app/components/workflow/nodes/list-filter/node.tsx @@ -4,7 +4,7 @@ import { useNodes } from 'reactflow' import { useTranslation } from 'react-i18next' import NodeVariableItem from '../variable-assigner/components/node-variable-item' import { type ListFilterNodeType } from './types' -import { isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' +import { isConversationVar, isENV, isSystemVar } from '@/app/components/workflow/nodes/_base/components/variable/utils' import { BlockEnum, type Node, type NodeProps } from '@/app/components/workflow/types' const i18nPrefix = 'workflow.nodes.listFilter' @@ -22,6 +22,7 @@ const NodeComponent: FC> = ({ const isSystem = isSystemVar(variable) const isEnv = isENV(variable) + const isChatVar = isConversationVar(variable) const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0]) const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.') return ( @@ -30,6 +31,7 @@ const NodeComponent: FC> = ({ From 9a3b7345c41d693884f53f7e06f550e9e747a1b7 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 11 Sep 2024 14:40:30 +0800 Subject: [PATCH 170/275] fix: split line too long --- .../prompt-editor/plugins/component-picker-block/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx index 0cadf9f95f..2624c309bb 100644 --- a/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx +++ b/web/app/components/base/prompt-editor/plugins/component-picker-block/index.tsx @@ -146,7 +146,7 @@ const ComponentPicker = ({ { // Divider index !== 0 && options.at(index - 1)?.group !== option.group && ( -
+
) } {option.renderMenuOption({ @@ -167,7 +167,7 @@ const ComponentPicker = ({ <> { (!!options.length) && ( -
+
) }
From 0076577764d36bba20eb1da56c78b98d9edc4c40 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Wed, 11 Sep 2024 18:25:14 +0800 Subject: [PATCH 171/275] file uploader --- .../base/chat/chat/chat-input-area/index.tsx | 4 +- .../file-from-link-or-local/index.tsx | 26 ++---- .../base/file-uploader/file-image-render.tsx | 30 +++++++ .../base/file-uploader/file-input.tsx | 21 +++++ .../file-list-flex-operation.tsx | 8 +- .../base/file-uploader/file-thumb.tsx | 7 ++ .../base/file-uploader/file-type-icon.tsx | 29 +++--- .../file-in-attachment-item.tsx | 89 +++++++++++++++---- .../file-uploader-in-attachment/index.tsx | 21 ++++- .../components/base/file-uploader/hooks.ts | 76 +++++++++------- .../components/base/file-uploader/store.tsx | 23 +++-- .../components/base/file-uploader/types.ts | 19 ++-- .../components/base/file-uploader/utils.ts | 40 ++++++++- .../share/text-generation/run-once/index.tsx | 2 +- 14 files changed, 284 insertions(+), 111 deletions(-) create mode 100644 web/app/components/base/file-uploader/file-image-render.tsx create mode 100644 web/app/components/base/file-uploader/file-input.tsx create mode 100644 web/app/components/base/file-uploader/file-thumb.tsx diff --git a/web/app/components/base/chat/chat/chat-input-area/index.tsx b/web/app/components/base/chat/chat/chat-input-area/index.tsx index 1cd0e87dbf..c8a4fcf59d 100644 --- a/web/app/components/base/chat/chat/chat-input-area/index.tsx +++ b/web/app/components/base/chat/chat/chat-input-area/index.tsx @@ -40,7 +40,7 @@ const ChatInputArea = ({ visionConfig, speechToTextConfig = { enabled: true }, onSend, - theme, + // theme, }: ChatInputAreaProps) => { const { t } = useTranslation() const { notify } = useToastContext() @@ -103,7 +103,7 @@ const ChatInputArea = ({ ) return ( - + {}}> <>
void showFromLocal?: boolean trigger: (open: boolean) => React.ReactNode } const FileFromLinkOrLocal = ({ showFromLink = true, - onLink, showFromLocal = true, trigger, }: FileFromLinkOrLocalProps) => { const { t } = useTranslation() const [open, setOpen] = useState(false) const [url, setUrl] = useState('') - const { handleLocalFileUpload } = useFile() - - const handleChange = (e: ChangeEvent) => { - const file = e.target.files?.[0] - - if (!file) - return - - handleLocalFileUpload(file) - } + const { handleLoadFileFromLink } = useFile() return ( setOpen(v => !v)} asChild> {trigger(open)} - +
{ showFromLink && ( @@ -65,7 +54,7 @@ const FileFromLinkOrLocal = ({ size='small' variant='primary' disabled={!url} - onClick={() => onLink?.(url)} + onClick={() => handleLoadFileFromLink()} > {t('common.operation.ok')} @@ -89,12 +78,7 @@ const FileFromLinkOrLocal = ({ > {t('common.fileUploader.uploadFromComputer')} - ((e.target as HTMLInputElement).value = '')} - type='file' - onChange={handleChange} - /> + ) } diff --git a/web/app/components/base/file-uploader/file-image-render.tsx b/web/app/components/base/file-uploader/file-image-render.tsx new file mode 100644 index 0000000000..864135af0f --- /dev/null +++ b/web/app/components/base/file-uploader/file-image-render.tsx @@ -0,0 +1,30 @@ +import cn from '@/utils/classnames' + +type FileImageRenderProps = { + imageUrl: string + className?: string + alt?: string + onLoad?: () => void + onError?: () => void +} +const FileImageRender = ({ + imageUrl, + className, + alt, + onLoad, + onError, +}: FileImageRenderProps) => { + return ( +
+ {alt} +
+ ) +} + +export default FileImageRender diff --git a/web/app/components/base/file-uploader/file-input.tsx b/web/app/components/base/file-uploader/file-input.tsx new file mode 100644 index 0000000000..42398a6a33 --- /dev/null +++ b/web/app/components/base/file-uploader/file-input.tsx @@ -0,0 +1,21 @@ +import { useFile } from './hooks' + +const FileInput = () => { + const { handleLocalFileUpload } = useFile() + const handleChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0] + + if (file) + handleLocalFileUpload(file) + } + return ( + ((e.target as HTMLInputElement).value = '')} + type='file' + onChange={handleChange} + /> + ) +} + +export default FileInput diff --git a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx index 583a4ff556..eb3bafc847 100644 --- a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx +++ b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx @@ -21,23 +21,23 @@ const FileListFlexOperation = forwardRef((_, ref) => { { files.map(file => (
{ - file._progress !== 100 && ( + file.progress !== 100 && (
{ + return ( +
+ ) +} + +export default FileThumb diff --git a/web/app/components/base/file-uploader/file-type-icon.tsx b/web/app/components/base/file-uploader/file-type-icon.tsx index 73d733f0cd..0808d6b95f 100644 --- a/web/app/components/base/file-uploader/file-type-icon.tsx +++ b/web/app/components/base/file-uploader/file-type-icon.tsx @@ -13,61 +13,62 @@ import { RiFileWordFill, RiMarkdownFill, } from '@remixicon/react' -import { FileTypeEnum } from './types' +import { FileAppearanceTypeEnum } from './types' +import type { FileAppearanceType } from './types' import cn from '@/utils/classnames' const FILE_TYPE_ICON_MAP = { - [FileTypeEnum.PDF]: { + [FileAppearanceTypeEnum.PDF]: { component: RiFilePdf2Fill, color: 'text-[#EA3434]', }, - [FileTypeEnum.IMAGE]: { + [FileAppearanceTypeEnum.IMAGE]: { component: RiFileImageFill, color: 'text-[#00B2EA]', }, - [FileTypeEnum.VIDEO]: { + [FileAppearanceTypeEnum.VIDEO]: { component: RiFileVideoFill, color: 'text-[#844FDA]', }, - [FileTypeEnum.AUDIO]: { + [FileAppearanceTypeEnum.AUDIO]: { component: RiFileMusicFill, color: 'text-[#FF3093]', }, - [FileTypeEnum.DOCUMENT]: { + [FileAppearanceTypeEnum.DOCUMENT]: { component: RiFileTextFill, color: 'text-[#6F8BB5]', }, - [FileTypeEnum.CODE]: { + [FileAppearanceTypeEnum.CODE]: { component: RiFileCodeFill, color: 'text-[#BCC0D1]', }, - [FileTypeEnum.MARKDOWN]: { + [FileAppearanceTypeEnum.MARKDOWN]: { component: RiMarkdownFill, color: 'text-[#309BEC]', }, - [FileTypeEnum.OTHER]: { + [FileAppearanceTypeEnum.OTHER]: { component: RiFile3Fill, color: 'text-[#BCC0D1]', }, - [FileTypeEnum.EXCEL]: { + [FileAppearanceTypeEnum.EXCEL]: { component: RiFileExcelFill, color: 'text-[#01AC49]', }, - [FileTypeEnum.WORD]: { + [FileAppearanceTypeEnum.WORD]: { component: RiFileWordFill, color: 'text-[#2684FF]', }, - [FileTypeEnum.PPT]: { + [FileAppearanceTypeEnum.PPT]: { component: RiFilePpt2Fill, color: 'text-[#FF650F]', }, - [FileTypeEnum.GIF]: { + [FileAppearanceTypeEnum.GIF]: { component: RiFileGifFill, color: 'text-[#00B2EA]', }, } type FileTypeIconProps = { - type: keyof typeof FileTypeEnum + type: FileAppearanceType size?: 'sm' | 'lg' className?: string } diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx index fe0d1a4987..4e617b8841 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx @@ -1,32 +1,91 @@ import { memo } from 'react' import { RiDeleteBinLine, + RiEditCircleFill, } from '@remixicon/react' import FileTypeIcon from '../file-type-icon' +import type { FileEntity } from '../types' +import { useFile } from '../hooks' +import { + getFileAppearanceType, + getFileExtension, + isImage, +} from '../utils' +import FileImageRender from '../file-image-render' import ActionButton from '@/app/components/base/action-button' import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' +import { formatFileSize } from '@/utils/format' +import cn from '@/utils/classnames' + +type FileInAttachmentItemProps = { + file: FileEntity +} +const FileInAttachmentItem = ({ + file, +}: FileInAttachmentItemProps) => { + const { + handleRemoveFile, + handleReUploadFile, + } = useFile() + const ext = getFileExtension(file.file) -const FileInAttachmentItem = () => { return ( -
- -
-
Yellow mountain range.jpg
+
+
+ { + isImage(file?.file) && ( + + ) + } + { + !isImage(file.file) && ( + + ) + } +
+
+
+ {file.file?.name} +
- JPG + { + ext && ( + {ext.toLowerCase()} + ) + } - 21.5 MB + {formatFileSize(file.file?.size || 0)}
- - + { + file.progress >= 0 && file.progress < 100 && ( + + ) + } + { + file.progress === -1 && ( + handleReUploadFile(file.id)}> + + + ) + } + handleRemoveFile(file.id)}>
diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx index c87dbab237..494a1101ae 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx @@ -11,6 +11,8 @@ import { FileContextProvider, useStore, } from '../store' +import type { FileEntity } from '../types' +import FileInput from '../file-input' import FileInAttachmentItem from './file-in-attachment-item' import Button from '@/app/components/base/button' import cn from '@/utils/classnames' @@ -41,10 +43,15 @@ const FileUploaderInAttachment = () => { ) }, []) @@ -75,7 +82,8 @@ const FileUploaderInAttachment = () => { { files.map(file => ( )) } @@ -84,9 +92,14 @@ const FileUploaderInAttachment = () => { ) } -const FileUploaderInAttachmentWrapper = () => { +type FileUploaderInAttachmentWrapperProps = { + onChange: (files: FileEntity[]) => void +} +const FileUploaderInAttachmentWrapper = ({ + onChange, +}: FileUploaderInAttachmentWrapperProps) => { return ( - + ) diff --git a/web/app/components/base/file-uploader/hooks.ts b/web/app/components/base/file-uploader/hooks.ts index fdae208e4e..1be14aaccd 100644 --- a/web/app/components/base/file-uploader/hooks.ts +++ b/web/app/components/base/file-uploader/hooks.ts @@ -3,6 +3,7 @@ import { useCallback, useState, } from 'react' +import { useParams } from 'next/navigation' import produce from 'immer' import { v4 as uuid4 } from 'uuid' import { useTranslation } from 'react-i18next' @@ -11,14 +12,11 @@ import { useFileStore } from './store' import { fileUpload } from './utils' import { useToastContext } from '@/app/components/base/toast' -type UseFileParams = { - isPublicAPI?: boolean - url?: string -} export const useFile = () => { const { t } = useTranslation() const { notify } = useToastContext() const fileStore = useFileStore() + const params = useParams() const handleAddOrUpdateFiles = useCallback((newFile: FileEntity) => { const { @@ -27,7 +25,7 @@ export const useFile = () => { } = fileStore.getState() const newFiles = produce(files, (draft) => { - const index = draft.findIndex(file => file._id === newFile._id) + const index = draft.findIndex(file => file.id === newFile.id) if (index > -1) draft[index] = newFile @@ -43,23 +41,44 @@ export const useFile = () => { setFiles, } = fileStore.getState() - const newFiles = files.filter(file => file._id !== fileId) + const newFiles = files.filter(file => file.id !== fileId) setFiles(newFiles) }, [fileStore]) - const handleLoadFileFromLink = useCallback((fileId: string, progress: number) => { + const handleReUploadFile = useCallback((fileId: string) => { const { files, setFiles, } = fileStore.getState() - const newFiles = produce(files, (draft) => { - const index = draft.findIndex(file => file._id === fileId) + const index = files.findIndex(file => file.id === fileId) - if (index > -1) - draft[index]._progress = progress - }) - setFiles(newFiles) - }, [fileStore]) + if (index > -1) { + const uploadingFile = files[index] + const newFiles = produce(files, (draft) => { + draft[index].progress = 0 + }) + setFiles(newFiles) + fileUpload({ + file: uploadingFile.file!, + onProgressCallback: (progress) => { + handleAddOrUpdateFiles({ ...uploadingFile, progress }) + }, + onSuccessCallback: (res) => { + handleAddOrUpdateFiles({ ...uploadingFile, fileId: res.id, progress: 100 }) + }, + onErrorCallback: () => { + notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') }) + handleAddOrUpdateFiles({ ...uploadingFile, progress: -1 }) + }, + }, !!params.token) + } + }, [fileStore, notify, t, handleAddOrUpdateFiles, params]) + + const handleLoadFileFromLink = useCallback(() => {}, []) + + const handleLoadFileFromLinkSuccess = useCallback(() => { }, []) + + const handleLoadFileFromLinkError = useCallback(() => { }, []) const handleClearFiles = useCallback(() => { const { @@ -68,39 +87,33 @@ export const useFile = () => { setFiles([]) }, [fileStore]) - const handleLocalFileUpload = useCallback(( - file: File, - { - isPublicAPI, - url, - }: UseFileParams = { isPublicAPI: false }, - ) => { + const handleLocalFileUpload = useCallback((file: File) => { const reader = new FileReader() const isImage = file.type.startsWith('image') reader.addEventListener( 'load', () => { const uploadingFile = { - _id: uuid4(), + id: uuid4(), file, - _url: reader.result as string, - _progress: 0, - _base64Url: isImage ? reader.result as string : '', + url: '', + progress: 0, + base64Url: isImage ? reader.result as string : '', } handleAddOrUpdateFiles(uploadingFile) fileUpload({ file: uploadingFile.file, onProgressCallback: (progress) => { - handleAddOrUpdateFiles({ ...uploadingFile, _progress: progress }) + handleAddOrUpdateFiles({ ...uploadingFile, progress }) }, onSuccessCallback: (res) => { - handleAddOrUpdateFiles({ ...uploadingFile, _fileId: res.id, _progress: 100 }) + handleAddOrUpdateFiles({ ...uploadingFile, fileId: res.id, progress: 100 }) }, onErrorCallback: () => { notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') }) - handleAddOrUpdateFiles({ ...uploadingFile, _progress: -1 }) + handleAddOrUpdateFiles({ ...uploadingFile, progress: -1 }) }, - }, isPublicAPI, url) + }, !!params.token) }, false, ) @@ -112,7 +125,7 @@ export const useFile = () => { false, ) reader.readAsDataURL(file) - }, [notify, t, handleAddOrUpdateFiles]) + }, [notify, t, handleAddOrUpdateFiles, params.token]) const handleClipboardPasteFile = useCallback((e: ClipboardEvent) => { const file = e.clipboardData?.files[0] @@ -154,7 +167,10 @@ export const useFile = () => { return { handleAddOrUpdateFiles, handleRemoveFile, + handleReUploadFile, handleLoadFileFromLink, + handleLoadFileFromLinkSuccess, + handleLoadFileFromLinkError, handleClearFiles, handleLocalFileUpload, handleClipboardPasteFile, diff --git a/web/app/components/base/file-uploader/store.tsx b/web/app/components/base/file-uploader/store.tsx index 1d58de145e..536fce1cf6 100644 --- a/web/app/components/base/file-uploader/store.tsx +++ b/web/app/components/base/file-uploader/store.tsx @@ -4,9 +4,9 @@ import { useRef, } from 'react' import { + create, useStore as useZustandStore, } from 'zustand' -import { createStore } from 'zustand/vanilla' import type { FileEntity } from './types' type Shape = { @@ -14,10 +14,15 @@ type Shape = { setFiles: (files: FileEntity[]) => void } -export const createFileStore = () => { - return createStore(set => ({ +export const createFileStore = ({ + onChange, +}: Pick) => { + return create(set => ({ files: [], - setFiles: files => set({ files }), + setFiles: (files) => { + set({ files }) + onChange(files) + }, })) } @@ -38,12 +43,18 @@ export const useFileStore = () => { type FileProviderProps = { children: React.ReactNode + onChange: (files: FileEntity[]) => void + isPublicAPI?: boolean + url?: string } -export const FileContextProvider = ({ children }: FileProviderProps) => { +export const FileContextProvider = ({ + children, + onChange, +}: FileProviderProps) => { const storeRef = useRef() if (!storeRef.current) - storeRef.current = createFileStore() + storeRef.current = createFileStore({ onChange }) return ( diff --git a/web/app/components/base/file-uploader/types.ts b/web/app/components/base/file-uploader/types.ts index a28152a4f8..237f444501 100644 --- a/web/app/components/base/file-uploader/types.ts +++ b/web/app/components/base/file-uploader/types.ts @@ -1,6 +1,4 @@ -import type { TransferMethod } from '@/types/app' - -export enum FileTypeEnum { +export enum FileAppearanceTypeEnum { IMAGE = 'IMAGE', VIDEO = 'VIDEO', AUDIO = 'AUDIO', @@ -15,12 +13,13 @@ export enum FileTypeEnum { OTHER = 'OTHER', } +export type FileAppearanceType = keyof typeof FileAppearanceTypeEnum + export type FileEntity = { - file: File - _id: string - _fileId?: string - _progress?: number - _url?: string - _base64Url?: string - _method?: TransferMethod + id: string + file?: File + fileId?: string + progress: number + url?: string + base64Url?: string } diff --git a/web/app/components/base/file-uploader/utils.ts b/web/app/components/base/file-uploader/utils.ts index 66e6668f76..10934c9fdf 100644 --- a/web/app/components/base/file-uploader/utils.ts +++ b/web/app/components/base/file-uploader/utils.ts @@ -1,3 +1,4 @@ +import { FileAppearanceTypeEnum } from './types' import { upload } from '@/service/base' type FileUploadParams = { @@ -35,8 +36,39 @@ export const fileUpload: FileUpload = ({ }) } -export const isFileType = (type: string) => { - return (file: File) => { - return file.type === type - } +export const getFileAppearanceType = (file?: File) => { + if (!file) + return FileAppearanceTypeEnum.OTHER + const mimeType = file.type + + if (mimeType.includes('image')) + return FileAppearanceTypeEnum.IMAGE + + if (mimeType.includes('video')) + return FileAppearanceTypeEnum.VIDEO + + if (mimeType.includes('audio')) + return FileAppearanceTypeEnum.AUDIO + + if (mimeType.includes('pdf')) + return FileAppearanceTypeEnum.PDF + + return FileAppearanceTypeEnum.OTHER +} + +export const isImage = (file?: File) => { + return file?.type.startsWith('image') +} + +export const getFileExtension = (file?: File) => { + if (!file) + return '' + + const fileNamePair = file.name.split('.') + const fileNamePairLength = fileNamePair.length + + if (fileNamePairLength > 1) + return fileNamePair[fileNamePairLength - 1] + + return '' } diff --git a/web/app/components/share/text-generation/run-once/index.tsx b/web/app/components/share/text-generation/run-once/index.tsx index 57ea2bea87..4924710be4 100644 --- a/web/app/components/share/text-generation/run-once/index.tsx +++ b/web/app/components/share/text-generation/run-once/index.tsx @@ -114,7 +114,7 @@ const RunOnce: FC = ({
) } - + {}} /> {promptConfig.prompt_variables.length > 0 && (
)} From a4c6d0b94b88aaf52ed2b1742249443110350187 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 13 Sep 2024 16:46:16 +0800 Subject: [PATCH 172/275] file uploader --- .../base/chat/chat/chat-input-area/index.tsx | 4 +- .../file-list-flex/file-list-flex-item.tsx | 51 ---------- .../file-list-flex-operation.tsx | 58 ----------- .../file-list-flex/file-list-flex-preview.tsx | 22 ----- .../file-in-attachment-item.tsx | 13 +-- .../file-image-item.tsx | 54 +++++++++++ .../file-uploader-in-chat-input/file-item.tsx | 96 +++++++++++++++++++ .../file-uploader-in-chat-input/file-list.tsx | 34 +++++++ .../components/base/file-uploader/index.ts | 2 +- .../icons/assets/vender/other/replay-line.svg | 5 + .../files/{Unknown.json => Unknow.json} | 2 +- .../public/files/{Unknown.tsx => Unknow.tsx} | 4 +- .../base/icons/src/public/files/index.ts | 2 +- .../icons/src/vender/other/ReplayLine.json | 36 +++++++ .../CuteRobot.tsx => other/ReplayLine.tsx} | 4 +- .../base/icons/src/vender/other/index.ts | 1 + .../{CuteRobot.json => CuteRobote.json} | 2 +- .../vender/solid/communication/CuteRobote.tsx | 16 ++++ .../src/vender/solid/communication/index.ts | 2 +- 19 files changed, 260 insertions(+), 148 deletions(-) delete mode 100644 web/app/components/base/file-uploader/file-list-flex/file-list-flex-item.tsx delete mode 100644 web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx delete mode 100644 web/app/components/base/file-uploader/file-list-flex/file-list-flex-preview.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx create mode 100644 web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx create mode 100644 web/app/components/base/icons/assets/vender/other/replay-line.svg rename web/app/components/base/icons/src/public/files/{Unknown.json => Unknow.json} (99%) rename web/app/components/base/icons/src/public/files/{Unknown.tsx => Unknow.tsx} (87%) create mode 100644 web/app/components/base/icons/src/vender/other/ReplayLine.json rename web/app/components/base/icons/src/vender/{solid/communication/CuteRobot.tsx => other/ReplayLine.tsx} (86%) rename web/app/components/base/icons/src/vender/solid/communication/{CuteRobot.json => CuteRobote.json} (98%) create mode 100644 web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx diff --git a/web/app/components/base/chat/chat/chat-input-area/index.tsx b/web/app/components/base/chat/chat/chat-input-area/index.tsx index c8a4fcf59d..f96d15b8ac 100644 --- a/web/app/components/base/chat/chat/chat-input-area/index.tsx +++ b/web/app/components/base/chat/chat/chat-input-area/index.tsx @@ -15,7 +15,7 @@ import type { Theme } from '../../embedded-chatbot/theme/theme-context' import { useTextAreaHeight } from './hooks' import Operation from './operation' import cn from '@/utils/classnames' -import { FileListFlexOperation } from '@/app/components/base/file-uploader' +import { FileListInChatInput } from '@/app/components/base/file-uploader' import { FileContextProvider } from '@/app/components/base/file-uploader/store' import VoiceInput from '@/app/components/base/voice-input' import { useToastContext } from '@/app/components/base/toast' @@ -111,7 +111,7 @@ const ChatInputArea = ({ )} >
- +
{ - if (isFile) { - return ( -
-
-
-
- - PDF -
·
- 3.9 MB -
- - - -
-
- ) - } - - return ( -
- ) -} - -export default memo(FileListFlexItem) diff --git a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx deleted file mode 100644 index eb3bafc847..0000000000 --- a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-operation.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { - forwardRef, - memo, -} from 'react' -import { RiCloseLine } from '@remixicon/react' -import { useStore } from '../store' -import { useFile } from '../hooks' -import FileListItem from './file-list-flex-item' -import Button from '@/app/components/base/button' -import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' - -const FileListFlexOperation = forwardRef((_, ref) => { - const files = useStore(s => s.files) - const { handleRemoveFile } = useFile() - - return ( -
- { - files.map(file => ( -
- - { - file.progress !== 100 && ( -
- -
- ) - } - -
- )) - } -
- ) -}) -FileListFlexOperation.displayName = 'FileListFlexOperation' - -export default memo(FileListFlexOperation) diff --git a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-preview.tsx b/web/app/components/base/file-uploader/file-list-flex/file-list-flex-preview.tsx deleted file mode 100644 index ca22f67dfd..0000000000 --- a/web/app/components/base/file-uploader/file-list-flex/file-list-flex-preview.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { - forwardRef, - memo, -} from 'react' -import FileListFlexItem from './file-list-flex-item' - -const FileListFlexPreview = forwardRef((_, ref) => { - return ( -
- - - - -
- ) -}) -FileListFlexPreview.displayName = 'FileListFlexPreview' - -export default memo(FileListFlexPreview) diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx index 4e617b8841..621fbca6c0 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx @@ -1,8 +1,5 @@ import { memo } from 'react' -import { - RiDeleteBinLine, - RiEditCircleFill, -} from '@remixicon/react' +import { RiDeleteBinLine } from '@remixicon/react' import FileTypeIcon from '../file-type-icon' import type { FileEntity } from '../types' import { useFile } from '../hooks' @@ -16,6 +13,7 @@ import ActionButton from '@/app/components/base/action-button' import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' import { formatFileSize } from '@/utils/format' import cn from '@/utils/classnames' +import { ReplayLine } from '@/app/components/base/icons/src/vender/other' type FileInAttachmentItemProps = { file: FileEntity @@ -80,8 +78,11 @@ const FileInAttachmentItem = ({ } { file.progress === -1 && ( - handleReUploadFile(file.id)}> - + handleReUploadFile(file.id)} + > + ) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx new file mode 100644 index 0000000000..2b0a86120d --- /dev/null +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx @@ -0,0 +1,54 @@ +import { RiCloseLine } from '@remixicon/react' +import FileImageRender from '../file-image-render' +import type { FileEntity } from '../types' +import { useFile } from '../hooks' +import Button from '@/app/components/base/button' +import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' +import { ReplayLine } from '@/app/components/base/icons/src/vender/other' + +type FileImageItemProps = { + file: FileEntity + className?: string +} +const FileImageItem = ({ + file, +}: FileImageItemProps) => { + const { handleRemoveFile } = useFile() + + return ( +
+ + + { + file.progress > 0 && file.progress < 100 && ( +
+ +
+ ) + } + { + file.progress === -1 && ( +
+ +
+ ) + } +
+ ) +} + +export default FileImageItem diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx new file mode 100644 index 0000000000..853f618587 --- /dev/null +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx @@ -0,0 +1,96 @@ +import { + RiCloseLine, + RiDownloadLine, +} from '@remixicon/react' +import type { FileEntity } from '../types' +import { + getFileAppearanceType, + getFileExtension, +} from '../utils' +import { useFile } from '../hooks' +import FileTypeIcon from '../file-type-icon' +import cn from '@/utils/classnames' +import { formatFileSize } from '@/utils/format' +import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' +import { ReplayLine } from '@/app/components/base/icons/src/vender/other' +import ActionButton from '@/app/components/base/action-button' +import Button from '@/app/components/base/button' + +type FileItemProps = { + file: FileEntity + showDownload?: boolean + className?: string +} +const FileItem = ({ + file, + showDownload, +}: FileItemProps) => { + const { handleRemoveFile } = useFile() + const ext = getFileExtension(file.file) + const uploadError = file.progress === -1 + + return ( +
+ +
+ {file.file?.name} +
+
+
+ + { + ext && ( + <> + {ext} +
·
+ + ) + } + {formatFileSize(file.file?.size || 0)} +
+ { + showDownload && ( + + + + ) + } + { + file.progress > 0 && file.progress < 100 && ( + + ) + } + { + file.progress === -1 && ( + + ) + } +
+
+ ) +} + +export default FileItem diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx new file mode 100644 index 0000000000..33fd4443e8 --- /dev/null +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx @@ -0,0 +1,34 @@ +import { isImage } from '../utils' +import { useStore } from '../store' +import FileImageItem from './file-image-item' +import FileItem from './file-item' + +const FileList = () => { + const files = useStore(s => s.files) + + return ( +
+ { + files.map((file) => { + if (isImage(file.file)) { + return ( + + ) + } + + return ( + + ) + }) + } +
+ ) +} + +export default FileList diff --git a/web/app/components/base/file-uploader/index.ts b/web/app/components/base/file-uploader/index.ts index 6ee9ec65fb..8d95504bf1 100644 --- a/web/app/components/base/file-uploader/index.ts +++ b/web/app/components/base/file-uploader/index.ts @@ -1,4 +1,4 @@ export { default as FileUploaderInAttachmentWrapper } from './file-uploader-in-attachment' export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input' export { default as FileTypeIcon } from './file-type-icon' -export { default as FileListFlexOperation } from './file-list-flex/file-list-flex-operation' +export { default as FileListInChatInput } from './file-uploader-in-chat-input/file-list' diff --git a/web/app/components/base/icons/assets/vender/other/replay-line.svg b/web/app/components/base/icons/assets/vender/other/replay-line.svg new file mode 100644 index 0000000000..c22074729e --- /dev/null +++ b/web/app/components/base/icons/assets/vender/other/replay-line.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/web/app/components/base/icons/src/public/files/Unknown.json b/web/app/components/base/icons/src/public/files/Unknow.json similarity index 99% rename from web/app/components/base/icons/src/public/files/Unknown.json rename to web/app/components/base/icons/src/public/files/Unknow.json index c39df990d0..33067fa96f 100644 --- a/web/app/components/base/icons/src/public/files/Unknown.json +++ b/web/app/components/base/icons/src/public/files/Unknow.json @@ -195,5 +195,5 @@ } ] }, - "name": "Unknown" + "name": "Unknow" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/files/Unknown.tsx b/web/app/components/base/icons/src/public/files/Unknow.tsx similarity index 87% rename from web/app/components/base/icons/src/public/files/Unknown.tsx rename to web/app/components/base/icons/src/public/files/Unknow.tsx index de909ed65e..ce84d344bf 100644 --- a/web/app/components/base/icons/src/public/files/Unknown.tsx +++ b/web/app/components/base/icons/src/public/files/Unknow.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './Unknown.json' +import data from './Unknow.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'Unknown' +Icon.displayName = 'Unknow' export default Icon diff --git a/web/app/components/base/icons/src/public/files/index.ts b/web/app/components/base/icons/src/public/files/index.ts index f38c28cbdb..2814c4ae39 100644 --- a/web/app/components/base/icons/src/public/files/index.ts +++ b/web/app/components/base/icons/src/public/files/index.ts @@ -6,6 +6,6 @@ export { default as Json } from './Json' export { default as Md } from './Md' export { default as Pdf } from './Pdf' export { default as Txt } from './Txt' -export { default as Unknown } from './Unknown' +export { default as Unknow } from './Unknow' export { default as Xlsx } from './Xlsx' export { default as Yaml } from './Yaml' diff --git a/web/app/components/base/icons/src/vender/other/ReplayLine.json b/web/app/components/base/icons/src/vender/other/ReplayLine.json new file mode 100644 index 0000000000..0fffbc98f5 --- /dev/null +++ b/web/app/components/base/icons/src/vender/other/ReplayLine.json @@ -0,0 +1,36 @@ +{ + "icon": { + "type": "element", + "isRootNode": true, + "name": "svg", + "attributes": { + "width": "20", + "height": "20", + "viewBox": "0 0 20 20", + "fill": "none", + "xmlns": "http://www.w3.org/2000/svg" + }, + "children": [ + { + "type": "element", + "name": "g", + "attributes": { + "id": "Retry" + }, + "children": [ + { + "type": "element", + "name": "path", + "attributes": { + "id": "Vector", + "d": "M9.99996 1.66669C14.6023 1.66669 18.3333 5.39765 18.3333 10C18.3333 14.6024 14.6023 18.3334 9.99996 18.3334C5.39758 18.3334 1.66663 14.6024 1.66663 10H3.33329C3.33329 13.6819 6.31806 16.6667 9.99996 16.6667C13.6819 16.6667 16.6666 13.6819 16.6666 10C16.6666 6.31812 13.6819 3.33335 9.99996 3.33335C7.70848 3.33335 5.68702 4.48947 4.48705 6.25022L6.66663 6.25002V7.91669H1.66663V2.91669H3.33329L3.3332 4.99934C4.85358 2.97565 7.2739 1.66669 9.99996 1.66669Z", + "fill": "currentColor" + }, + "children": [] + } + ] + } + ] + }, + "name": "ReplayLine" +} \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx b/web/app/components/base/icons/src/vender/other/ReplayLine.tsx similarity index 86% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx rename to web/app/components/base/icons/src/vender/other/ReplayLine.tsx index 49994048b7..7dabfc71af 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx +++ b/web/app/components/base/icons/src/vender/other/ReplayLine.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './CuteRobot.json' +import data from './ReplayLine.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'CuteRobot' +Icon.displayName = 'ReplayLine' export default Icon diff --git a/web/app/components/base/icons/src/vender/other/index.ts b/web/app/components/base/icons/src/vender/other/index.ts index db6fd9e3f1..1982dcfa3a 100644 --- a/web/app/components/base/icons/src/vender/other/index.ts +++ b/web/app/components/base/icons/src/vender/other/index.ts @@ -1 +1,2 @@ export { default as Generator } from './Generator' +export { default as ReplayLine } from './ReplayLine' diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json similarity index 98% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json rename to web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json index 5b36575f56..389d044a9b 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json @@ -34,5 +34,5 @@ } ] }, - "name": "CuteRobot" + "name": "CuteRobote" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx new file mode 100644 index 0000000000..d416fb5b66 --- /dev/null +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx @@ -0,0 +1,16 @@ +// GENERATE BY script +// DON NOT EDIT IT MANUALLY + +import * as React from 'react' +import data from './CuteRobote.json' +import IconBase from '@/app/components/base/icons/IconBase' +import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' + +const Icon = React.forwardRef, Omit>(( + props, + ref, +) => ) + +Icon.displayName = 'CuteRobote' + +export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/communication/index.ts b/web/app/components/base/icons/src/vender/solid/communication/index.ts index 673de27463..854953c116 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/index.ts +++ b/web/app/components/base/icons/src/vender/solid/communication/index.ts @@ -1,6 +1,6 @@ export { default as AiText } from './AiText' export { default as ChatBot } from './ChatBot' -export { default as CuteRobot } from './CuteRobot' +export { default as CuteRobote } from './CuteRobote' export { default as EditList } from './EditList' export { default as MessageDotsCircle } from './MessageDotsCircle' export { default as MessageFast } from './MessageFast' From 5dd556b4c8fdd13418fcc5cd4fa55e5ff6aff48d Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Fri, 13 Sep 2024 17:31:10 +0800 Subject: [PATCH 173/275] file uploader --- ...e-in-attachment-item.tsx => file-item.tsx} | 73 +++++++++++++------ .../file-uploader-in-attachment/index.tsx | 20 ++++- .../file-image-item.tsx | 48 +++++++----- .../file-uploader-in-chat-input/file-item.tsx | 55 ++++++++------ .../file-uploader-in-chat-input/file-list.tsx | 24 +++++- .../components/base/file-uploader/hooks.ts | 10 +-- .../components/base/file-uploader/index.ts | 2 + .../components/base/file-uploader/types.ts | 6 +- 8 files changed, 158 insertions(+), 80 deletions(-) rename web/app/components/base/file-uploader/file-uploader-in-attachment/{file-in-attachment-item.tsx => file-item.tsx} (58%) diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-item.tsx similarity index 58% rename from web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx rename to web/app/components/base/file-uploader/file-uploader-in-attachment/file-item.tsx index 621fbca6c0..00fbf402dc 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/file-in-attachment-item.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/file-item.tsx @@ -1,8 +1,9 @@ import { memo } from 'react' -import { RiDeleteBinLine } from '@remixicon/react' +import { + RiDeleteBinLine, + RiDownloadLine, +} from '@remixicon/react' import FileTypeIcon from '../file-type-icon' -import type { FileEntity } from '../types' -import { useFile } from '../hooks' import { getFileAppearanceType, getFileExtension, @@ -16,35 +17,46 @@ import cn from '@/utils/classnames' import { ReplayLine } from '@/app/components/base/icons/src/vender/other' type FileInAttachmentItemProps = { - file: FileEntity + fileId: string + file: File + imageUrl?: string + progress?: number + showDeleteAction?: boolean + showDownloadAction?: boolean + onRemove?: (fileId: string) => void + onReUpload?: (fileId: string) => void } const FileInAttachmentItem = ({ + fileId, file, + imageUrl, + progress = 0, + showDeleteAction, + showDownloadAction = true, + onRemove, + onReUpload, }: FileInAttachmentItemProps) => { - const { - handleRemoveFile, - handleReUploadFile, - } = useFile() - const ext = getFileExtension(file.file) + const isImageFile = isImage(file) + const ext = getFileExtension(file) return (
{ - isImage(file?.file) && ( + isImageFile && ( ) } { - !isImage(file.file) && ( + !isImageFile && ( ) @@ -53,9 +65,9 @@ const FileInAttachmentItem = ({
- {file.file?.name} + {file.name}
{ @@ -64,31 +76,44 @@ const FileInAttachmentItem = ({ ) } - {formatFileSize(file.file?.size || 0)} + {formatFileSize(file.size || 0)}
{ - file.progress >= 0 && file.progress < 100 && ( + progress > 0 && progress < 100 && ( ) } { - file.progress === -1 && ( + progress === -1 && ( handleReUploadFile(file.id)} + onClick={() => onReUpload?.(fileId)} > ) } - handleRemoveFile(file.id)}> - - + { + showDeleteAction && ( + onRemove?.(fileId)}> + + + ) + } + { + showDownloadAction && ( + + + + ) + }
) diff --git a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx index 494a1101ae..c451746f06 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-attachment/index.tsx @@ -13,7 +13,8 @@ import { } from '../store' import type { FileEntity } from '../types' import FileInput from '../file-input' -import FileInAttachmentItem from './file-in-attachment-item' +import { useFile } from '../hooks' +import FileItem from './file-item' import Button from '@/app/components/base/button' import cn from '@/utils/classnames' @@ -25,6 +26,10 @@ type Option = { const FileUploaderInAttachment = () => { const { t } = useTranslation() const files = useStore(s => s.files) + const { + handleRemoveFile, + handleReUploadFile, + } = useFile() const options = [ { value: 'local', @@ -81,9 +86,16 @@ const FileUploaderInAttachment = () => {
{ files.map(file => ( - handleRemoveFile(file.fileId)} + onReUpload={() => handleReUploadFile(file.fileId)} /> )) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx index 2b0a86120d..a1095fefe7 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-image-item.tsx @@ -1,37 +1,46 @@ import { RiCloseLine } from '@remixicon/react' import FileImageRender from '../file-image-render' -import type { FileEntity } from '../types' -import { useFile } from '../hooks' import Button from '@/app/components/base/button' import ProgressCircle from '@/app/components/base/progress-bar/progress-circle' import { ReplayLine } from '@/app/components/base/icons/src/vender/other' type FileImageItemProps = { - file: FileEntity - className?: string + fileId: string + imageUrl?: string + progress?: number + showDeleteAction?: boolean + onRemove?: (fileId: string) => void + onReUpload?: (fileId: string) => void } const FileImageItem = ({ - file, + fileId, + imageUrl, + progress = 0, + showDeleteAction, + onRemove, + onReUpload, }: FileImageItemProps) => { - const { handleRemoveFile } = useFile() - return (
- + { + showDeleteAction && ( + + ) + } { - file.progress > 0 && file.progress < 100 && ( + progress > 0 && progress < 100 && (
- + onReUpload?.(fileId)} + />
) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx index 853f618587..38af9d0136 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-item.tsx @@ -2,12 +2,10 @@ import { RiCloseLine, RiDownloadLine, } from '@remixicon/react' -import type { FileEntity } from '../types' import { getFileAppearanceType, getFileExtension, } from '../utils' -import { useFile } from '../hooks' import FileTypeIcon from '../file-type-icon' import cn from '@/utils/classnames' import { formatFileSize } from '@/utils/format' @@ -17,17 +15,25 @@ import ActionButton from '@/app/components/base/action-button' import Button from '@/app/components/base/button' type FileItemProps = { - file: FileEntity - showDownload?: boolean - className?: string + fileId: string + file: File + progress?: number + showDeleteAction?: boolean + showDownloadAction?: boolean + onRemove?: (fileId: string) => void + onReUpload?: (fileId: string) => void } const FileItem = ({ + fileId, file, - showDownload, + progress = 0, + showDeleteAction, + showDownloadAction = true, + onRemove, + onReUpload, }: FileItemProps) => { - const { handleRemoveFile } = useFile() - const ext = getFileExtension(file.file) - const uploadError = file.progress === -1 + const ext = getFileExtension(file) + const uploadError = progress === -1 return (
- + { + showDeleteAction && ( + + ) + }
- {file.file?.name} + {file.name}
{ @@ -62,10 +72,10 @@ const FileItem = ({ ) } - {formatFileSize(file.file?.size || 0)} + {formatFileSize(file.size || 0)}
{ - showDownload && ( + showDownloadAction && ( @@ -74,17 +84,18 @@ const FileItem = ({ ) } { - file.progress > 0 && file.progress < 100 && ( + progress > 0 && progress < 100 && ( ) } { - file.progress === -1 && ( + uploadError && ( onReUpload?.(fileId)} /> ) } diff --git a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx index 33fd4443e8..870de729dc 100644 --- a/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx +++ b/web/app/components/base/file-uploader/file-uploader-in-chat-input/file-list.tsx @@ -1,10 +1,15 @@ import { isImage } from '../utils' +import { useFile } from '../hooks' import { useStore } from '../store' import FileImageItem from './file-image-item' import FileItem from './file-item' const FileList = () => { const files = useStore(s => s.files) + const { + handleRemoveFile, + handleReUploadFile, + } = useFile() return (
@@ -13,16 +18,27 @@ const FileList = () => { if (isImage(file.file)) { return ( ) } return ( ) }) diff --git a/web/app/components/base/file-uploader/hooks.ts b/web/app/components/base/file-uploader/hooks.ts index 1be14aaccd..1bec18265c 100644 --- a/web/app/components/base/file-uploader/hooks.ts +++ b/web/app/components/base/file-uploader/hooks.ts @@ -25,7 +25,7 @@ export const useFile = () => { } = fileStore.getState() const newFiles = produce(files, (draft) => { - const index = draft.findIndex(file => file.id === newFile.id) + const index = draft.findIndex(file => file.fileId === newFile.fileId) if (index > -1) draft[index] = newFile @@ -41,7 +41,7 @@ export const useFile = () => { setFiles, } = fileStore.getState() - const newFiles = files.filter(file => file.id !== fileId) + const newFiles = files.filter(file => file.fileId !== fileId) setFiles(newFiles) }, [fileStore]) @@ -50,7 +50,7 @@ export const useFile = () => { files, setFiles, } = fileStore.getState() - const index = files.findIndex(file => file.id === fileId) + const index = files.findIndex(file => file.fileId === fileId) if (index > -1) { const uploadingFile = files[index] @@ -94,7 +94,7 @@ export const useFile = () => { 'load', () => { const uploadingFile = { - id: uuid4(), + fileId: uuid4(), file, url: '', progress: 0, @@ -107,7 +107,7 @@ export const useFile = () => { handleAddOrUpdateFiles({ ...uploadingFile, progress }) }, onSuccessCallback: (res) => { - handleAddOrUpdateFiles({ ...uploadingFile, fileId: res.id, progress: 100 }) + handleAddOrUpdateFiles({ ...uploadingFile, fileStorageId: res.id, progress: 100 }) }, onErrorCallback: () => { notify({ type: 'error', message: t('common.imageUploader.uploadFromComputerUploadError') }) diff --git a/web/app/components/base/file-uploader/index.ts b/web/app/components/base/file-uploader/index.ts index 8d95504bf1..1c599cd4b0 100644 --- a/web/app/components/base/file-uploader/index.ts +++ b/web/app/components/base/file-uploader/index.ts @@ -1,4 +1,6 @@ export { default as FileUploaderInAttachmentWrapper } from './file-uploader-in-attachment' +export { default as FileItemInAttachment } from './file-uploader-in-attachment/file-item' export { default as FileUploaderInChatInput } from './file-uploader-in-chat-input' export { default as FileTypeIcon } from './file-type-icon' export { default as FileListInChatInput } from './file-uploader-in-chat-input/file-list' +export { default as FileItemInChatInput } from './file-uploader-in-chat-input/file-list' diff --git a/web/app/components/base/file-uploader/types.ts b/web/app/components/base/file-uploader/types.ts index 237f444501..217959433b 100644 --- a/web/app/components/base/file-uploader/types.ts +++ b/web/app/components/base/file-uploader/types.ts @@ -16,9 +16,9 @@ export enum FileAppearanceTypeEnum { export type FileAppearanceType = keyof typeof FileAppearanceTypeEnum export type FileEntity = { - id: string - file?: File - fileId?: string + fileId: string + file: File + fileStorageId?: string progress: number url?: string base64Url?: string From a10b0db1022dd43d6736a28b873865433c0cb608 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Thu, 12 Sep 2024 14:13:45 +0800 Subject: [PATCH 174/275] vision setting --- .../app/configuration/config-vision/index.tsx | 30 +- .../config-vision/param-config-content.tsx | 137 +++++++ .../config-vision/param-config.tsx | 41 ++ .../configuration/debug/chat-user-input.tsx | 1 - .../debug/debug-with-multiple-model/index.tsx | 30 +- .../debug/debug-with-single-model/index.tsx | 4 +- .../components/app/configuration/index.tsx | 2 +- .../prompt-value-panel/index.tsx | 2 +- web/app/components/base/chat/chat/index.tsx | 24 +- .../moderation/moderation-setting-modal.tsx | 376 ------------------ .../feature-panel/opening-statement/index.tsx | 321 --------------- web/app/components/base/features/types.ts | 6 +- 12 files changed, 251 insertions(+), 723 deletions(-) create mode 100644 web/app/components/app/configuration/config-vision/param-config-content.tsx create mode 100644 web/app/components/app/configuration/config-vision/param-config.tsx delete mode 100644 web/app/components/base/features/feature-panel/moderation/moderation-setting-modal.tsx delete mode 100644 web/app/components/base/features/feature-panel/opening-statement/index.tsx diff --git a/web/app/components/app/configuration/config-vision/index.tsx b/web/app/components/app/configuration/config-vision/index.tsx index ae388615c0..7666fcb834 100644 --- a/web/app/components/app/configuration/config-vision/index.tsx +++ b/web/app/components/app/configuration/config-vision/index.tsx @@ -4,12 +4,15 @@ import React, { useCallback } from 'react' import { useTranslation } from 'react-i18next' import produce from 'immer' import { useContext } from 'use-context-selector' +import ParamConfig from './param-config' import { Vision } from '@/app/components/base/icons/src/vender/features' import Tooltip from '@/app/components/base/tooltip' -import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' +// import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' import ConfigContext from '@/context/debug-configuration' -import { Resolution } from '@/types/app' +// import { Resolution } from '@/types/app' import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' +import Switch from '@/app/components/base/switch' +import type { FileUpload } from '@/app/components/base/features/types' const ConfigVision: FC = () => { const { t } = useTranslation() @@ -17,7 +20,7 @@ const ConfigVision: FC = () => { const file = useFeatures(s => s.features.file) const featuresStore = useFeaturesStore() - const handleChange = useCallback((resolution: Resolution) => { + const handleChange = useCallback((data: FileUpload) => { const { features, setFeatures, @@ -26,7 +29,8 @@ const ConfigVision: FC = () => { const newFeatures = produce(features, (draft) => { draft.file = { ...draft.file, - image: { detail: resolution }, + enabled: data.enabled, + image: { detail: data.image?.detail }, } }) setFeatures(newFeatures) @@ -53,7 +57,7 @@ const ConfigVision: FC = () => { />
-
+ {/*
{t('appDebug.vision.visionSettings.resolution')}
{
} /> -
-
+
*/} + {/*
{ selected={file?.image?.detail === Resolution.low} onSelect={() => handleChange(Resolution.low)} /> -
+
*/} + +
+ handleChange({ + ...(file || {}), + enabled: value, + })} + size='md' + />
) diff --git a/web/app/components/app/configuration/config-vision/param-config-content.tsx b/web/app/components/app/configuration/config-vision/param-config-content.tsx new file mode 100644 index 0000000000..97195b24d7 --- /dev/null +++ b/web/app/components/app/configuration/config-vision/param-config-content.tsx @@ -0,0 +1,137 @@ +'use client' +import type { FC } from 'react' +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import produce from 'immer' +import OptionCard from '@/app/components/workflow/nodes/_base/components/option-card' +import { Resolution, TransferMethod } from '@/types/app' +import ParamItem from '@/app/components/base/param-item' +import Tooltip from '@/app/components/base/tooltip' +import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks' +import type { FileUpload } from '@/app/components/base/features/types' + +const MIN = 1 +const MAX = 6 +const ParamConfigContent: FC = () => { + const { t } = useTranslation() + const file = useFeatures(s => s.features.file) + const featuresStore = useFeaturesStore() + + const handleChange = useCallback((data: FileUpload) => { + const { + features, + setFeatures, + } = featuresStore!.getState() + + const newFeatures = produce(features, (draft) => { + draft.file = { + ...draft.file, + image: { detail: data.image?.detail }, + allowed_file_upload_methods: data.allowed_file_upload_methods, + number_limits: data.number_limits, + } + }) + setFeatures(newFeatures) + }, [featuresStore]) + + return ( +
+
{t('appDebug.vision.visionSettings.title')}
+
+
+
+
{t('appDebug.vision.visionSettings.resolution')}
+ + {t('appDebug.vision.visionSettings.resolutionTooltip').split('\n').map(item => ( +
{item}
+ ))} +
+ } + /> +
+
+ handleChange({ + ...file, + image: { detail: Resolution.high }, + })} + /> + handleChange({ + ...file, + image: { detail: Resolution.low }, + })} + /> +
+
+
+
{t('appDebug.vision.visionSettings.uploadMethod')}
+
+ handleChange({ + ...file, + allowed_file_upload_methods: [TransferMethod.local_file, TransferMethod.remote_url], + })} + /> + handleChange({ + ...file, + allowed_file_upload_methods: [TransferMethod.local_file], + })} + /> + handleChange({ + ...file, + allowed_file_upload_methods: [TransferMethod.remote_url], + })} + /> +
+
+
+ { + if (!value) + return + + handleChange({ + ...file, + number_limits: value, + }) + }} + /> +
+
+
+ ) +} + +export default React.memo(ParamConfigContent) diff --git a/web/app/components/app/configuration/config-vision/param-config.tsx b/web/app/components/app/configuration/config-vision/param-config.tsx new file mode 100644 index 0000000000..8c63879391 --- /dev/null +++ b/web/app/components/app/configuration/config-vision/param-config.tsx @@ -0,0 +1,41 @@ +'use client' +import type { FC } from 'react' +import { memo, useState } from 'react' +import { useTranslation } from 'react-i18next' +import ParamConfigContent from './param-config-content' +import cn from '@/utils/classnames' +import { Settings01 } from '@/app/components/base/icons/src/vender/line/general' +import { + PortalToFollowElem, + PortalToFollowElemContent, + PortalToFollowElemTrigger, +} from '@/app/components/base/portal-to-follow-elem' + +const ParamsConfig: FC = () => { + const { t } = useTranslation() + const [open, setOpen] = useState(false) + + return ( + + setOpen(v => !v)}> +
+ +
{t('appDebug.voice.settings')}
+
+
+ +
+ +
+
+
+ ) +} +export default memo(ParamsConfig) diff --git a/web/app/components/app/configuration/debug/chat-user-input.tsx b/web/app/components/app/configuration/debug/chat-user-input.tsx index 8dec6635bc..cda41917e3 100644 --- a/web/app/components/app/configuration/debug/chat-user-input.tsx +++ b/web/app/components/app/configuration/debug/chat-user-input.tsx @@ -49,7 +49,6 @@ const ChatUserInput = ({ return (
- {/* ##TODO## file_upload */} {promptVariables.map(({ key, name, type, options, max_length, required }, index) => (
{ const { mode, - isShowVisionConfig, + // isShowVisionConfig, } = useDebugConfigurationContext() const speech2text = useFeatures(s => s.features.speech2text) const file = useFeatures(s => s.features.file) @@ -32,6 +34,15 @@ const DebugWithMultipleModel = () => { const { eventEmitter } = useEventEmitterContextContext() const isChatMode = mode === 'chat' || mode === 'agent-chat' + const visionConfig = useMemo(() => { + return { + enabled: file?.enabled || false, + detail: file?.image?.detail || Resolution.high, + number_limits: file?.number_limits || 3, + transfer_methods: file?.allowed_file_upload_methods || [TransferMethod.local_file, TransferMethod.remote_url], + } + }, [file]) + const handleSend = useCallback((message: string, files?: VisionFile[]) => { if (checkCanSend && !checkCanSend()) return @@ -95,7 +106,7 @@ const DebugWithMultipleModel = () => { } }, [twoLine, threeLine, fourLine]) - const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal) + // const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal) return (
@@ -128,14 +139,19 @@ const DebugWithMultipleModel = () => {
{isChatMode && (
- + {/* + /> */}
)}
diff --git a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx index 7fdb3e2046..456a77ebbb 100644 --- a/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx +++ b/web/app/components/app/configuration/debug/debug-with-single-model/index.tsx @@ -41,7 +41,7 @@ const DebugWithSingleModel = forwardRef s.features) @@ -142,7 +142,7 @@ const DebugWithSingleModel = forwardRef { = ({
))} - {/* ##TODO## file_upload */} {visionConfig?.enabled && (
{t('common.imageUploader.imageUpload')}
@@ -204,6 +203,7 @@ const PromptValuePanel: FC = ({
diff --git a/web/app/components/base/chat/chat/index.tsx b/web/app/components/base/chat/chat/index.tsx index 6e305553ba..0f95e8c97d 100644 --- a/web/app/components/base/chat/chat/index.tsx +++ b/web/app/components/base/chat/chat/index.tsx @@ -6,6 +6,7 @@ import { memo, useCallback, useEffect, + useMemo, useRef, useState, } from 'react' @@ -21,7 +22,7 @@ import type { import type { ThemeBuilder } from '../embedded-chatbot/theme/theme-context' import Question from './question' import Answer from './answer' -// import ChatInput from './chat-input' +import ChatInput from './chat-input' import ChatInputArea from './chat-input-area' import TryToAsk from './try-to-ask' import { ChatContextProvider } from './context' @@ -33,6 +34,7 @@ import AgentLogModal from '@/app/components/base/agent-log-modal' import PromptLogModal from '@/app/components/base/prompt-log-modal' import { useStore as useAppStore } from '@/app/components/app/store' import type { AppData } from '@/models/share' +import { Resolution, TransferMethod } from '@/types/app' export type ChatProps = { appData?: AppData @@ -182,6 +184,15 @@ const Chat: FC = ({ const hasTryToAsk = config?.suggested_questions_after_answer?.enabled && !!suggestedQuestions?.length && onSend + const visionConfig = useMemo(() => { + return { + enabled: (config?.file_upload as any)?.enabled || false, + detail: (config?.file_upload as any)?.image?.detail || Resolution.high, + number_limits: (config?.file_upload as any)?.number_limits || 3, + transfer_methods: (config?.file_upload as any)?.allowed_file_upload_methods || [TransferMethod.local_file, TransferMethod.remote_url], + } + }, [config?.file_upload]) + return ( = ({ ) } { - !noChatInput && ( + !noChatInput && showFileUpload && ( = ({ speechToTextConfig={config?.speech_to_text} onSend={onSend} theme={themeBuilder?.theme} - // noSpacing={noSpacing} /> ) } + {!noChatInput && !showFileUpload && ( + + )}
{showPromptLogModal && !hideLogModal && ( diff --git a/web/app/components/base/features/feature-panel/moderation/moderation-setting-modal.tsx b/web/app/components/base/features/feature-panel/moderation/moderation-setting-modal.tsx deleted file mode 100644 index 635506c053..0000000000 --- a/web/app/components/base/features/feature-panel/moderation/moderation-setting-modal.tsx +++ /dev/null @@ -1,376 +0,0 @@ -import type { ChangeEvent, FC } from 'react' -import { - memo, - useState, -} from 'react' -import useSWR from 'swr' -import { useContext } from 'use-context-selector' -import { useTranslation } from 'react-i18next' -import ModerationContent from './moderation-content' -import FormGeneration from './form-generation' -import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector' -import Modal from '@/app/components/base/modal' -import Button from '@/app/components/base/button' -import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education' -import type { ModerationConfig, ModerationContentConfig } from '@/models/debug' -import { useToastContext } from '@/app/components/base/toast' -import { - fetchCodeBasedExtensionList, - fetchModelProviders, -} from '@/service/common' -import type { CodeBasedExtensionItem } from '@/models/common' -import I18n from '@/context/i18n' -import { LanguagesSupported } from '@/i18n/language' -import { InfoCircle } from '@/app/components/base/icons/src/vender/line/general' -import { useModalContext } from '@/context/modal-context' -import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' - -const systemTypes = ['openai_moderation', 'keywords', 'api'] - -type Provider = { - key: string - name: string - form_schema?: CodeBasedExtensionItem['form_schema'] -} - -type ModerationSettingModalProps = { - data: ModerationConfig - onCancel: () => void - onSave: (moderationConfig: ModerationConfig) => void -} - -const ModerationSettingModal: FC = ({ - data, - onCancel, - onSave, -}) => { - const { t } = useTranslation() - const { notify } = useToastContext() - const { locale } = useContext(I18n) - const { data: modelProviders, isLoading, mutate } = useSWR('/workspaces/current/model-providers', fetchModelProviders) - const [localeData, setLocaleData] = useState(data) - const { setShowAccountSettingModal } = useModalContext() - const handleOpenSettingsModal = () => { - setShowAccountSettingModal({ - payload: 'provider', - onCancelCallback: () => { - mutate() - }, - }) - } - const { data: codeBasedExtensionList } = useSWR( - '/code-based-extension?module=moderation', - fetchCodeBasedExtensionList, - ) - const openaiProvider = modelProviders?.data.find(item => item.provider === 'openai') - const systemOpenaiProviderEnabled = openaiProvider?.system_configuration.enabled - const systemOpenaiProviderQuota = systemOpenaiProviderEnabled ? openaiProvider?.system_configuration.quota_configurations.find(item => item.quota_type === openaiProvider.system_configuration.current_quota_type) : undefined - const systemOpenaiProviderCanUse = systemOpenaiProviderQuota?.is_valid - const customOpenaiProvidersCanUse = openaiProvider?.custom_configuration.status === CustomConfigurationStatusEnum.active - const isOpenAIProviderConfigured = customOpenaiProvidersCanUse || systemOpenaiProviderCanUse - const providers: Provider[] = [ - { - key: 'openai_moderation', - name: t('appDebug.feature.moderation.modal.provider.openai'), - }, - { - key: 'keywords', - name: t('appDebug.feature.moderation.modal.provider.keywords'), - }, - { - key: 'api', - name: t('common.apiBasedExtension.selector.title'), - }, - ...( - codeBasedExtensionList - ? codeBasedExtensionList.data.map((item) => { - return { - key: item.name, - name: locale === 'zh-Hans' ? item.label['zh-Hans'] : item.label['en-US'], - form_schema: item.form_schema, - } - }) - : [] - ), - ] - - const currentProvider = providers.find(provider => provider.key === localeData.type) - - const handleDataTypeChange = (type: string) => { - let config: undefined | Record - const currProvider = providers.find(provider => provider.key === type) - - if (systemTypes.findIndex(t => t === type) < 0 && currProvider?.form_schema) { - config = currProvider?.form_schema.reduce((prev, next) => { - prev[next.variable] = next.default - return prev - }, {} as Record) - } - setLocaleData({ - ...localeData, - type, - config, - }) - } - - const handleDataKeywordsChange = (e: ChangeEvent) => { - const value = e.target.value - - const arr = value.split('\n').reduce((prev: string[], next: string) => { - if (next !== '') - prev.push(next.slice(0, 100)) - if (next === '' && prev[prev.length - 1] !== '') - prev.push(next) - - return prev - }, []) - - setLocaleData({ - ...localeData, - config: { - ...localeData.config, - keywords: arr.slice(0, 100).join('\n'), - }, - }) - } - - const handleDataContentChange = (contentType: string, contentConfig: ModerationContentConfig) => { - setLocaleData({ - ...localeData, - config: { - ...localeData.config, - [contentType]: contentConfig, - }, - }) - } - - const handleDataApiBasedChange = (apiBasedExtensionId: string) => { - setLocaleData({ - ...localeData, - config: { - ...localeData.config, - api_based_extension_id: apiBasedExtensionId, - }, - }) - } - - const handleDataExtraChange = (extraValue: Record) => { - setLocaleData({ - ...localeData, - config: { - ...localeData.config, - ...extraValue, - }, - }) - } - - const formatData = (originData: ModerationConfig) => { - const { enabled, type, config } = originData - const { inputs_config, outputs_config } = config! - const params: Record = {} - - if (type === 'keywords') - params.keywords = config?.keywords - - if (type === 'api') - params.api_based_extension_id = config?.api_based_extension_id - - if (systemTypes.findIndex(t => t === type) < 0 && currentProvider?.form_schema) { - currentProvider.form_schema.forEach((form) => { - params[form.variable] = config?.[form.variable] - }) - } - - return { - type, - enabled, - config: { - inputs_config: inputs_config || { enabled: false }, - outputs_config: outputs_config || { enabled: false }, - ...params, - }, - } - } - - const handleSave = () => { - if (localeData.type === 'openai_moderation' && !isOpenAIProviderConfigured) - return - - if (!localeData.config?.inputs_config?.enabled && !localeData.config?.outputs_config?.enabled) { - notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.condition') }) - return - } - - if (localeData.type === 'keywords' && !localeData.config.keywords) { - notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }) }) - return - } - - if (localeData.type === 'api' && !localeData.config.api_based_extension_id) { - notify({ type: 'error', message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }) }) - return - } - - if (systemTypes.findIndex(t => t === localeData.type) < 0 && currentProvider?.form_schema) { - for (let i = 0; i < currentProvider.form_schema.length; i++) { - if (!localeData.config?.[currentProvider.form_schema[i].variable] && currentProvider.form_schema[i].required) { - notify({ - type: 'error', - message: t('appDebug.errorMessage.valueOfVarRequired', { key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }), - }) - return - } - } - } - - if (localeData.config.inputs_config?.enabled && !localeData.config.inputs_config.preset_response && localeData.type !== 'api') { - notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') }) - return - } - - if (localeData.config.outputs_config?.enabled && !localeData.config.outputs_config.preset_response && localeData.type !== 'api') { - notify({ type: 'error', message: t('appDebug.feature.moderation.modal.content.errorMessage') }) - return - } - - onSave(formatData(localeData)) - } - - return ( - { }} - className='!p-8 !pb-6 !mt-14 !max-w-none !w-[640px]' - > -
- {t('appDebug.feature.moderation.modal.title')} -
-
-
- {t('appDebug.feature.moderation.modal.provider.title')} -
-
- { - providers.map(provider => ( -
handleDataTypeChange(provider.key)} - > -
- {provider.name} -
- )) - } -
- { - !isLoading && !isOpenAIProviderConfigured && localeData.type === 'openai_moderation' && ( -
- -
- {t('appDebug.feature.moderation.modal.openaiNotConfig.before')} - -  {t('common.settings.provider')}  - - {t('appDebug.feature.moderation.modal.openaiNotConfig.after')} -
-
- ) - } -
- { - localeData.type === 'keywords' && ( -
-
{t('appDebug.feature.moderation.modal.provider.keywords')}
-
{t('appDebug.feature.moderation.modal.keywords.tip')}
-
- -
- ) - : ( -
- )} - {renderQuestions()} - ) : ( -
{t('appDebug.openingStatement.noDataPlaceHolder')}
- )} - - {isShowConfirmAddVar && ( - - )} - -
- - ) -} -export default React.memo(OpeningStatement) diff --git a/web/app/components/base/features/types.ts b/web/app/components/base/features/types.ts index 0b959c4aa4..3307f12cda 100644 --- a/web/app/components/base/features/types.ts +++ b/web/app/components/base/features/types.ts @@ -1,4 +1,4 @@ -import type { TransferMethod, TtsAutoPlay } from '@/types/app' +import type { Resolution, TransferMethod, TtsAutoPlay } from '@/types/app' export type EnabledOrDisabled = { enabled?: boolean @@ -30,13 +30,13 @@ export type SensitiveWordAvoidance = EnabledOrDisabled & { export type FileUpload = { image?: EnabledOrDisabled & { - detail?: 'high' | 'low' + detail?: Resolution number_limits?: number transfer_methods?: TransferMethod[] } allowed_file_types?: string[] allowed_file_extensions?: string[] - allowed_file_upload_methods?: string[] + allowed_file_upload_methods?: TransferMethod[] number_limits?: number } & EnabledOrDisabled From 96d2582d891d1195d605fb5ed4e1a385eb65d15c Mon Sep 17 00:00:00 2001 From: JzoNg Date: Fri, 13 Sep 2024 16:06:25 +0800 Subject: [PATCH 175/275] file var in form --- .../configuration/config-var/config-modal/index.tsx | 12 ++++++------ .../config-var/select-type-item/index.tsx | 2 +- .../base/chat/chat/chat-input-area/operation.tsx | 2 +- .../new-feature-panel/conversation-opener/modal.tsx | 2 +- .../workflow/hooks/use-nodes-sync-draft.ts | 4 ++-- .../_base/components/before-run-form/form-item.tsx | 6 +++++- .../nodes/_base/components/file-type-item.tsx | 8 ++++---- .../nodes/_base/components/file-upload-setting.tsx | 8 ++++---- .../panel/debug-and-preview/chat-wrapper.tsx | 9 +++++---- .../workflow/panel/debug-and-preview/index.tsx | 1 + 10 files changed, 30 insertions(+), 24 deletions(-) diff --git a/web/app/components/app/configuration/config-var/config-modal/index.tsx b/web/app/components/app/configuration/config-var/config-modal/index.tsx index 194ee12e2e..f8510a5cb8 100644 --- a/web/app/components/app/configuration/config-var/config-modal/index.tsx +++ b/web/app/components/app/configuration/config-var/config-modal/index.tsx @@ -153,12 +153,12 @@ const ConfigModal: FC = ({ } else if ([InputVarType.singleFile, InputVarType.multiFiles].includes(type)) { if (tempPayload.allowed_file_types?.length === 0) { - const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConig.file.supportFileTypes') }) + const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConfig.file.supportFileTypes') }) Toast.notify({ type: 'error', message: errorMessages }) return } if (tempPayload.allowed_file_types?.includes(SupportUploadFileTypes.custom) && !tempPayload.allowed_file_extensions?.length) { - const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConig.file.custom.name') }) + const errorMessages = t('workflow.errorMsg.fieldRequired', { field: t('appDebug.variableConfig.file.custom.name') }) Toast.notify({ type: 'error', message: errorMessages }) return } @@ -178,7 +178,7 @@ const ConfigModal: FC = ({
- +
@@ -191,7 +191,7 @@ const ConfigModal: FC = ({
- + handlePayloadChange('variable')(e.target.value)} @@ -199,7 +199,7 @@ const ConfigModal: FC = ({ placeholder={t('appDebug.variableConfig.inputPlaceholder')!} /> - + handlePayloadChange('label')(e.target.value)} @@ -229,7 +229,7 @@ const ConfigModal: FC = ({
handlePayloadChange('required')(!tempPayload.required)} /> - {t('appDebug.variableConig.required')} + {t('appDebug.variableConfig.required')}
diff --git a/web/app/components/app/configuration/config-var/select-type-item/index.tsx b/web/app/components/app/configuration/config-var/select-type-item/index.tsx index f96e80cb78..b71486b4eb 100644 --- a/web/app/components/app/configuration/config-var/select-type-item/index.tsx +++ b/web/app/components/app/configuration/config-var/select-type-item/index.tsx @@ -22,7 +22,7 @@ const SelectTypeItem: FC = ({ onClick, }) => { const { t } = useTranslation() - const typeName = t(`appDebug.variableConig.${i18nFileTypeMap[type] || type}`) + const typeName = t(`appDebug.variableConfig.${i18nFileTypeMap[type] || type}`) return (
(({ ref={ref} >
- + {visionConfig?.enabled && } { speechToTextConfig?.enabled && ( { setTempSuggestedQuestions([...tempSuggestedQuestions, '']) }} className='mt-1 flex items-center h-9 px-3 gap-2 rounded-lg cursor-pointer text-gray-400 bg-gray-100 hover:bg-gray-200'> -
{t('appDebug.variableConig.addOption')}
+
{t('appDebug.variableConfig.addOption')}
)}
diff --git a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts index 203386f5a7..a25a4f1812 100644 --- a/web/app/components/workflow/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow/hooks/use-nodes-sync-draft.ts @@ -75,8 +75,8 @@ export const useNodesSyncDraft = () => { }, }, features: { - opening_statement: features.opening?.opening_statement || '', - suggested_questions: features.opening?.suggested_questions || [], + opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '', + suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [], suggested_questions_after_answer: features.suggested, text_to_speech: features.text2speech, speech_to_text: features.speech2text, diff --git a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx index 6b66fe89cf..30b6a66171 100644 --- a/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/before-run-form/form-item.tsx @@ -15,6 +15,7 @@ import Select from '@/app/components/base/select' import Input from '@/app/components/base/input' import Textarea from '@/app/components/base/textarea' import TextGenerationImageUploader from '@/app/components/base/image-uploader/text-generation-image-uploader' +import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader' import { Resolution } from '@/types/app' import { useFeatures } from '@/app/components/base/features/hooks' import { VarBlockIcon } from '@/app/components/workflow/block-icon' @@ -156,7 +157,10 @@ const FormItem: FC = ({ ) } - {/* #TODO# file type new form */} + {/* #TODO# file upload */} + {(type === InputVarType.singleFile || type === InputVarType.multiFiles) && ( + {}} /> + )} { type === InputVarType.files && ( = ({
-
{t(`appDebug.variableConig.file.${type}.name`)}
+
{t(`appDebug.variableConfig.file.${type}.name`)}
e.stopPropagation()}>
@@ -63,8 +63,8 @@ const FileTypeItem: FC = ({ {/* TODO: Wait File type icon */}
-
{t(`appDebug.variableConig.file.${type}.name`)}
-
{type !== SupportUploadFileTypes.custom ? FILE_EXTS[type].join(', ') : t('appDebug.variableConig.file.custom.description')}
+
{t(`appDebug.variableConfig.file.${type}.name`)}
+
{type !== SupportUploadFileTypes.custom ? FILE_EXTS[type].join(', ') : t('appDebug.variableConfig.file.custom.description')}
diff --git a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx index d915f59036..21f33aad0a 100644 --- a/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx +++ b/web/app/components/workflow/nodes/_base/components/file-upload-setting.tsx @@ -87,7 +87,7 @@ const FileUploadSetting: FC = ({
{!inFeaturePanel && (
{ @@ -135,10 +135,10 @@ const FileUploadSetting: FC = ({ {isMultiple && (
-
{t('appDebug.variableConig.maxNumberTip')}
+
{t('appDebug.variableConfig.maxNumberTip')}
= ({ )} {inFeaturePanel && (
diff --git a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx index 3b71fa284d..101125c290 100644 --- a/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/chat-wrapper.tsx @@ -42,8 +42,8 @@ const ChatWrapper = forwardRef(({ showConv const features = useFeatures(s => s.features) const config = useMemo(() => { return { - opening_statement: features.opening?.opening_statement || '', - suggested_questions: features.opening?.suggested_questions || [], + opening_statement: features.opening?.enabled ? (features.opening?.opening_statement || '') : '', + suggested_questions: features.opening?.enabled ? (features.opening?.suggested_questions || []) : [], suggested_questions_after_answer: features.suggested, text_to_speech: features.text2speech, speech_to_text: features.speech2text, @@ -51,7 +51,7 @@ const ChatWrapper = forwardRef(({ showConv sensitive_word_avoidance: features.moderation, file_upload: features.file, } - }, [features]) + }, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file]) const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel) const { @@ -104,7 +104,8 @@ const ChatWrapper = forwardRef(({ showConv chatContainerClassName='px-3' chatContainerInnerClassName='pt-6 w-full max-w-full mx-auto' chatFooterClassName='px-4 rounded-bl-2xl' - chatFooterInnerClassName='pb-2' + chatFooterInnerClassName='pb-0' + showFileUpload showFeatureBar onFeatureBarClick={setShowFeaturesPanel} onSend={doSend} diff --git a/web/app/components/workflow/panel/debug-and-preview/index.tsx b/web/app/components/workflow/panel/debug-and-preview/index.tsx index c6647d79b8..08db7d8092 100644 --- a/web/app/components/workflow/panel/debug-and-preview/index.tsx +++ b/web/app/components/workflow/panel/debug-and-preview/index.tsx @@ -85,6 +85,7 @@ const DebugAndPreview = () => { + {expanded &&
}
)}
From 5ec604500c09a909539decb534ebb275ff109fc0 Mon Sep 17 00:00:00 2001 From: Joel Date: Wed, 18 Sep 2024 11:15:36 +0800 Subject: [PATCH 176/275] chore: change field to backend --- web/app/components/workflow/nodes/doc-extractor/default.ts | 4 ++-- web/app/components/workflow/nodes/doc-extractor/node.tsx | 2 +- web/app/components/workflow/nodes/doc-extractor/panel.tsx | 2 +- web/app/components/workflow/nodes/doc-extractor/types.ts | 2 +- web/app/components/workflow/nodes/doc-extractor/use-config.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/app/components/workflow/nodes/doc-extractor/default.ts b/web/app/components/workflow/nodes/doc-extractor/default.ts index 204b1bcb5a..21d01abaf7 100644 --- a/web/app/components/workflow/nodes/doc-extractor/default.ts +++ b/web/app/components/workflow/nodes/doc-extractor/default.ts @@ -6,7 +6,7 @@ const i18nPrefix = 'workflow.errorMsg' const nodeDefault: NodeDefault = { defaultValue: { - variable: [], + variable_selector: [], }, getAvailablePrevNodes(isChatMode: boolean) { const nodes = isChatMode @@ -20,7 +20,7 @@ const nodeDefault: NodeDefault = { }, checkValid(payload: DocExtractorNodeType, t: any) { let errorMessages = '' - const { variable } = payload + const { variable_selector: variable } = payload if (!errorMessages && !variable?.length) errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') }) diff --git a/web/app/components/workflow/nodes/doc-extractor/node.tsx b/web/app/components/workflow/nodes/doc-extractor/node.tsx index 655162acab..6324961051 100644 --- a/web/app/components/workflow/nodes/doc-extractor/node.tsx +++ b/web/app/components/workflow/nodes/doc-extractor/node.tsx @@ -15,7 +15,7 @@ const NodeComponent: FC> = ({ const { t } = useTranslation() const nodes: Node[] = useNodes() - const { variable } = data + const { variable_selector: variable } = data if (!variable || variable.length === 0) return null diff --git a/web/app/components/workflow/nodes/doc-extractor/panel.tsx b/web/app/components/workflow/nodes/doc-extractor/panel.tsx index 9f520735e9..83714a4bcf 100644 --- a/web/app/components/workflow/nodes/doc-extractor/panel.tsx +++ b/web/app/components/workflow/nodes/doc-extractor/panel.tsx @@ -33,7 +33,7 @@ const Panel: FC> = ({ readonly={readOnly} nodeId={id} isShowNodeName - value={inputs.variable || []} + value={inputs.variable_selector || []} onChange={handleVarChanges} filterVar={filterVar} /> diff --git a/web/app/components/workflow/nodes/doc-extractor/types.ts b/web/app/components/workflow/nodes/doc-extractor/types.ts index 054e4b6cf2..96f29b407e 100644 --- a/web/app/components/workflow/nodes/doc-extractor/types.ts +++ b/web/app/components/workflow/nodes/doc-extractor/types.ts @@ -1,5 +1,5 @@ import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types' export type DocExtractorNodeType = CommonNodeType & { - variable: ValueSelector + variable_selector: ValueSelector } diff --git a/web/app/components/workflow/nodes/doc-extractor/use-config.ts b/web/app/components/workflow/nodes/doc-extractor/use-config.ts index 2bb2897b96..e72217e9ad 100644 --- a/web/app/components/workflow/nodes/doc-extractor/use-config.ts +++ b/web/app/components/workflow/nodes/doc-extractor/use-config.ts @@ -15,7 +15,7 @@ const useConfig = (id: string, payload: DocExtractorNodeType) => { const handleVarChanges = useCallback((variable: ValueSelector | string) => { const newInputs = produce(inputs, (draft) => { - draft.variable = variable as ValueSelector + draft.variable_selector = variable as ValueSelector }) setInputs(newInputs) }, [inputs, setInputs]) From 54105e85ff7dd283a1a3a33f2a3861742b2cdb64 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 18 Sep 2024 11:20:49 +0800 Subject: [PATCH 177/275] fix icon --- .../solid/communication/{cute-robote.svg => cute-robot.svg} | 0 .../solid/communication/{CuteRobote.json => CuteRobot.json} | 2 +- .../solid/communication/{CuteRobote.tsx => CuteRobot.tsx} | 4 ++-- .../base/icons/src/vender/solid/communication/index.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename web/app/components/base/icons/assets/vender/solid/communication/{cute-robote.svg => cute-robot.svg} (100%) rename web/app/components/base/icons/src/vender/solid/communication/{CuteRobote.json => CuteRobot.json} (98%) rename web/app/components/base/icons/src/vender/solid/communication/{CuteRobote.tsx => CuteRobot.tsx} (86%) diff --git a/web/app/components/base/icons/assets/vender/solid/communication/cute-robote.svg b/web/app/components/base/icons/assets/vender/solid/communication/cute-robot.svg similarity index 100% rename from web/app/components/base/icons/assets/vender/solid/communication/cute-robote.svg rename to web/app/components/base/icons/assets/vender/solid/communication/cute-robot.svg diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json b/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json similarity index 98% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json rename to web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json index 389d044a9b..5b36575f56 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.json +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.json @@ -34,5 +34,5 @@ } ] }, - "name": "CuteRobote" + "name": "CuteRobot" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx b/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx similarity index 86% rename from web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx rename to web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx index d416fb5b66..49994048b7 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/CuteRobote.tsx +++ b/web/app/components/base/icons/src/vender/solid/communication/CuteRobot.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './CuteRobote.json' +import data from './CuteRobot.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'CuteRobote' +Icon.displayName = 'CuteRobot' export default Icon diff --git a/web/app/components/base/icons/src/vender/solid/communication/index.ts b/web/app/components/base/icons/src/vender/solid/communication/index.ts index 854953c116..673de27463 100644 --- a/web/app/components/base/icons/src/vender/solid/communication/index.ts +++ b/web/app/components/base/icons/src/vender/solid/communication/index.ts @@ -1,6 +1,6 @@ export { default as AiText } from './AiText' export { default as ChatBot } from './ChatBot' -export { default as CuteRobote } from './CuteRobote' +export { default as CuteRobot } from './CuteRobot' export { default as EditList } from './EditList' export { default as MessageDotsCircle } from './MessageDotsCircle' export { default as MessageFast } from './MessageFast' From affb2e38a1b47bef9a6808b9ca33b5d8d87ce1df Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 18 Sep 2024 13:14:18 +0800 Subject: [PATCH 178/275] fix typo of icon path --- .../icons/assets/public/files/{unknow.svg => unknown.svg} | 0 .../base/icons/src/public/files/{Unknow.json => Unknown.json} | 2 +- .../base/icons/src/public/files/{Unknow.tsx => Unknown.tsx} | 4 ++-- web/app/components/base/icons/src/public/files/index.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename web/app/components/base/icons/assets/public/files/{unknow.svg => unknown.svg} (100%) rename web/app/components/base/icons/src/public/files/{Unknow.json => Unknown.json} (99%) rename web/app/components/base/icons/src/public/files/{Unknow.tsx => Unknown.tsx} (87%) diff --git a/web/app/components/base/icons/assets/public/files/unknow.svg b/web/app/components/base/icons/assets/public/files/unknown.svg similarity index 100% rename from web/app/components/base/icons/assets/public/files/unknow.svg rename to web/app/components/base/icons/assets/public/files/unknown.svg diff --git a/web/app/components/base/icons/src/public/files/Unknow.json b/web/app/components/base/icons/src/public/files/Unknown.json similarity index 99% rename from web/app/components/base/icons/src/public/files/Unknow.json rename to web/app/components/base/icons/src/public/files/Unknown.json index 33067fa96f..c39df990d0 100644 --- a/web/app/components/base/icons/src/public/files/Unknow.json +++ b/web/app/components/base/icons/src/public/files/Unknown.json @@ -195,5 +195,5 @@ } ] }, - "name": "Unknow" + "name": "Unknown" } \ No newline at end of file diff --git a/web/app/components/base/icons/src/public/files/Unknow.tsx b/web/app/components/base/icons/src/public/files/Unknown.tsx similarity index 87% rename from web/app/components/base/icons/src/public/files/Unknow.tsx rename to web/app/components/base/icons/src/public/files/Unknown.tsx index ce84d344bf..de909ed65e 100644 --- a/web/app/components/base/icons/src/public/files/Unknow.tsx +++ b/web/app/components/base/icons/src/public/files/Unknown.tsx @@ -2,7 +2,7 @@ // DON NOT EDIT IT MANUALLY import * as React from 'react' -import data from './Unknow.json' +import data from './Unknown.json' import IconBase from '@/app/components/base/icons/IconBase' import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase' @@ -11,6 +11,6 @@ const Icon = React.forwardRef, Omit ) -Icon.displayName = 'Unknow' +Icon.displayName = 'Unknown' export default Icon diff --git a/web/app/components/base/icons/src/public/files/index.ts b/web/app/components/base/icons/src/public/files/index.ts index 2814c4ae39..f38c28cbdb 100644 --- a/web/app/components/base/icons/src/public/files/index.ts +++ b/web/app/components/base/icons/src/public/files/index.ts @@ -6,6 +6,6 @@ export { default as Json } from './Json' export { default as Md } from './Md' export { default as Pdf } from './Pdf' export { default as Txt } from './Txt' -export { default as Unknow } from './Unknow' +export { default as Unknown } from './Unknown' export { default as Xlsx } from './Xlsx' export { default as Yaml } from './Yaml' From 9f66e6e357828d71332400247efc657059424e49 Mon Sep 17 00:00:00 2001 From: JzoNg Date: Wed, 18 Sep 2024 13:36:11 +0800 Subject: [PATCH 179/275] fix feature bar in basic chabot --- .../debug/debug-with-multiple-model/index.tsx | 7 +++++-- .../components/base/chat/chat/chat-input.tsx | 19 +++++++++++++------ web/app/components/base/chat/chat/index.tsx | 12 ++++++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx index 91614c6a93..9957649c32 100644 --- a/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx +++ b/web/app/components/app/configuration/debug/debug-with-multiple-model/index.tsx @@ -17,7 +17,7 @@ import ChatInput from '@/app/components/base/chat/chat/chat-input' import type { VisionFile } from '@/app/components/base/chat/types' import { useDebugConfigurationContext } from '@/context/debug-configuration' import { useFeatures } from '@/app/components/base/features/hooks' -// import { useStore as useAppStore } from '@/app/components/app/store' +import { useStore as useAppStore } from '@/app/components/app/store' import { Resolution, TransferMethod } from '@/types/app' const DebugWithMultipleModel = () => { @@ -106,7 +106,7 @@ const DebugWithMultipleModel = () => { } }, [twoLine, threeLine, fourLine]) - // const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal) + const setShowAppConfigureFeaturesModal = useAppStore(s => s.setShowAppConfigureFeaturesModal) return (
@@ -140,6 +140,9 @@ const DebugWithMultipleModel = () => { {isChatMode && (
void visionConfig?: VisionConfig speechToTextConfig?: EnableType onSend?: OnSend @@ -42,6 +47,10 @@ type ChatInputProps = { noSpacing?: boolean } const ChatInput: FC = ({ + showFeatureBar, + showFileUpload, + featureBarDisabled, + onFeatureBarClick, visionConfig, speechToTextConfig, onSend, @@ -149,12 +158,9 @@ const ChatInput: FC = ({ return ( <> -
+
{ visionConfig?.enabled && ( @@ -180,7 +186,7 @@ const ChatInput: FC = ({ ) }