void
- itemClassName?: string
- disable?: boolean
-}
-
-const PermissionsRadio = ({
- value,
- onChange,
- itemClassName,
- disable,
-}: IPermissionsRadioProps) => {
- const { t } = useTranslation()
- const options = [
- {
- key: 'only_me',
- text: t('datasetSettings.form.permissionsOnlyMe'),
- },
- {
- key: 'all_team_members',
- text: t('datasetSettings.form.permissionsAllMember'),
- },
- ]
-
- return (
-
- {
- options.map(option => (
-
{
- if (!disable)
- onChange(option.key as DataSet['permission'])
- }}
- >
-
-
{option.text}
-
-
- ))
- }
-
- )
-}
-
-export default PermissionsRadio
diff --git a/web/app/components/develop/template/template.en.mdx b/web/app/components/develop/template/template.en.mdx
index ea3b7f3d23..31c812958f 100755
--- a/web/app/components/develop/template/template.en.mdx
+++ b/web/app/components/develop/template/template.en.mdx
@@ -192,8 +192,8 @@ The text generation application offers non-session support and is ideal for tran
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": "'m", "created_at": 1679586595}
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " glad", "created_at": 1679586595}
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " to", "created_at": 1679586595}
- data: {"event": "message", "message_id": : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " meet", "created_at": 1679586595}
- data: {"event": "message", "message_id": : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " you", "created_at": 1679586595}
+ data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " meet", "created_at": 1679586595}
+ data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " you", "created_at": 1679586595}
data: {"event": "message_end", "id": "5e52ce04-874b-4d27-9045-b3bc80def685", "metadata": {"usage": {"prompt_tokens": 1033, "prompt_unit_price": "0.001", "prompt_price_unit": "0.001", "prompt_price": "0.0010330", "completion_tokens": 135, "completion_unit_price": "0.002", "completion_price_unit": "0.001", "completion_price": "0.0002700", "total_tokens": 1168, "total_price": "0.0013030", "currency": "USD", "latency": 1.381760165997548}}}
data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"}
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
@@ -400,7 +400,7 @@ The text generation application offers non-session support and is ideal for tran
For text messages generated by Dify, simply pass the generated message-id directly. The backend will use the message-id to look up the corresponding content and synthesize the voice information directly. If both message_id and text are provided simultaneously, the message_id is given priority.
- Speech generated content。
+ Speech generated content.
The user identifier, defined by the developer, must ensure uniqueness within the app.
diff --git a/web/app/components/workflow/blocks.tsx b/web/app/components/workflow/blocks.tsx
new file mode 100644
index 0000000000..334ddbf087
--- /dev/null
+++ b/web/app/components/workflow/blocks.tsx
@@ -0,0 +1,5 @@
+import { BlockEnum } from './types'
+
+export const ALL_AVAILABLE_BLOCKS = Object.values(BlockEnum)
+export const ALL_CHAT_AVAILABLE_BLOCKS = ALL_AVAILABLE_BLOCKS.filter(key => key !== BlockEnum.End && key !== BlockEnum.Start) as BlockEnum[]
+export const ALL_COMPLETION_AVAILABLE_BLOCKS = ALL_AVAILABLE_BLOCKS.filter(key => key !== BlockEnum.Answer && key !== BlockEnum.Start) as BlockEnum[]
diff --git a/web/app/components/workflow/constants.ts b/web/app/components/workflow/constants.ts
index d04163b853..5f52a75464 100644
--- a/web/app/components/workflow/constants.ts
+++ b/web/app/components/workflow/constants.ts
@@ -203,9 +203,6 @@ export const NODES_EXTRA_DATA: Record = {
}
-export const ALL_CHAT_AVAILABLE_BLOCKS = Object.keys(NODES_EXTRA_DATA).filter(key => key !== BlockEnum.End && key !== BlockEnum.Start) as BlockEnum[]
-export const ALL_COMPLETION_AVAILABLE_BLOCKS = Object.keys(NODES_EXTRA_DATA).filter(key => key !== BlockEnum.Answer && key !== BlockEnum.Start) as BlockEnum[]
-
export const NODES_INITIAL_DATA = {
[BlockEnum.Start]: {
type: BlockEnum.Start,
diff --git a/web/app/components/workflow/nodes/_base/components/node-control.tsx b/web/app/components/workflow/nodes/_base/components/node-control.tsx
index 1ce78220a1..781b66af6b 100644
--- a/web/app/components/workflow/nodes/_base/components/node-control.tsx
+++ b/web/app/components/workflow/nodes/_base/components/node-control.tsx
@@ -45,13 +45,13 @@ const NodeControl: FC = ({
`}
>
e.stopPropagation()}
>
{
canRunBySingle(data.type) && (
{
handleNodeDataUpdate({
id,
diff --git a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
index 4c19236716..43d65d3439 100644
--- a/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
+++ b/web/app/components/workflow/nodes/_base/components/panel-operator/index.tsx
@@ -54,12 +54,12 @@ const PanelOperator = ({
-
+
diff --git a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx
index a5718ba2b6..f17d34af8a 100644
--- a/web/app/components/workflow/nodes/_base/components/title-description-input.tsx
+++ b/web/app/components/workflow/nodes/_base/components/title-description-input.tsx
@@ -33,10 +33,8 @@ export const TitleInput = memo(({
value={localValue}
onChange={e => setLocalValue(e.target.value)}
className={`
- grow mr-2 px-1 h-6 text-base text-gray-900 font-semibold rounded-lg border border-transparent appearance-none outline-none
- hover:bg-gray-50
- focus:border-gray-300 focus:shadow-xs focus:bg-white caret-[#295EFF]
- min-w-0
+ grow mr-2 px-1 h-7 text-text-primary system-xl-semibold rounded-md border border-transparent appearance-none outline-none
+ focus:shadow-xs min-w-0
`}
placeholder={t('workflow.common.addTitle') || ''}
onBlur={handleBlur}
@@ -66,8 +64,8 @@ export const DescriptionInput = memo(({
)
- : (
+ : (
{isSupportConstantValue
?
{
e.stopPropagation()
diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx
index 4807fa3b2b..e13a7a4117 100644
--- a/web/app/components/workflow/nodes/_base/node.tsx
+++ b/web/app/components/workflow/nodes/_base/node.tsx
@@ -107,7 +107,7 @@ const BaseNode: FC
= ({
'group relative pb-1 shadow-xs',
'border border-transparent rounded-[15px]',
data.type !== BlockEnum.Iteration && 'w-[240px] bg-workflow-block-bg',
- data.type === BlockEnum.Iteration && 'flex flex-col w-full h-full bg-[#fcfdff]/80',
+ data.type === BlockEnum.Iteration && 'flex flex-col w-full h-full bg-workflow-block-bg-transparent border-workflow-block-border',
!data._runningStatus && 'hover:shadow-lg',
showRunningBorder && '!border-state-accent-solid',
showSuccessBorder && '!border-state-success-solid',
@@ -169,7 +169,7 @@ const BaseNode: FC = ({
}
= {
defaultValue: {
diff --git a/web/app/components/workflow/nodes/assigner/default.ts b/web/app/components/workflow/nodes/assigner/default.ts
index 99f0a1c3d1..f443ae1d3b 100644
--- a/web/app/components/workflow/nodes/assigner/default.ts
+++ b/web/app/components/workflow/nodes/assigner/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import { type AssignerNodeType, WriteMode } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/code/default.ts b/web/app/components/workflow/nodes/code/default.ts
index fa9b9398a4..5f90c18716 100644
--- a/web/app/components/workflow/nodes/code/default.ts
+++ b/web/app/components/workflow/nodes/code/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import { CodeLanguage, type CodeNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
diff --git a/web/app/components/workflow/nodes/document-extractor/default.ts b/web/app/components/workflow/nodes/document-extractor/default.ts
index 54045cc52e..4ffc64b72b 100644
--- a/web/app/components/workflow/nodes/document-extractor/default.ts
+++ b/web/app/components/workflow/nodes/document-extractor/default.ts
@@ -1,7 +1,7 @@
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'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/end/default.ts b/web/app/components/workflow/nodes/end/default.ts
index ceeda5b43b..25abfb5849 100644
--- a/web/app/components/workflow/nodes/end/default.ts
+++ b/web/app/components/workflow/nodes/end/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import { type EndNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const nodeDefault: NodeDefault = {
defaultValue: {
diff --git a/web/app/components/workflow/nodes/http/default.ts b/web/app/components/workflow/nodes/http/default.ts
index f506c934a2..1bd584eeb9 100644
--- a/web/app/components/workflow/nodes/http/default.ts
+++ b/web/app/components/workflow/nodes/http/default.ts
@@ -5,7 +5,7 @@ import type { BodyPayload, HttpNodeType } from './types'
import {
ALL_CHAT_AVAILABLE_BLOCKS,
ALL_COMPLETION_AVAILABLE_BLOCKS,
-} from '@/app/components/workflow/constants'
+} from '@/app/components/workflow/blocks'
const nodeDefault: NodeDefault = {
defaultValue: {
diff --git a/web/app/components/workflow/nodes/if-else/default.ts b/web/app/components/workflow/nodes/if-else/default.ts
index 1c994a37d4..8d98f694bd 100644
--- a/web/app/components/workflow/nodes/if-else/default.ts
+++ b/web/app/components/workflow/nodes/if-else/default.ts
@@ -2,7 +2,7 @@ import { BlockEnum, type NodeDefault } from '../../types'
import { type IfElseNodeType, LogicalOperator } from './types'
import { isEmptyRelatedOperator } from './utils'
import { TransferMethod } from '@/types/app'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/iteration-start/default.ts b/web/app/components/workflow/nodes/iteration-start/default.ts
index d98efa7ba2..c93b472259 100644
--- a/web/app/components/workflow/nodes/iteration-start/default.ts
+++ b/web/app/components/workflow/nodes/iteration-start/default.ts
@@ -1,6 +1,6 @@
import type { NodeDefault } from '../../types'
import type { IterationStartNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const nodeDefault: NodeDefault = {
defaultValue: {},
diff --git a/web/app/components/workflow/nodes/iteration-start/index.tsx b/web/app/components/workflow/nodes/iteration-start/index.tsx
index a765f82733..58174e1262 100644
--- a/web/app/components/workflow/nodes/iteration-start/index.tsx
+++ b/web/app/components/workflow/nodes/iteration-start/index.tsx
@@ -9,7 +9,7 @@ const IterationStartNode = ({ id, data }: NodeProps) => {
const { t } = useTranslation()
return (
-
+
diff --git a/web/app/components/workflow/nodes/iteration/add-block.tsx b/web/app/components/workflow/nodes/iteration/add-block.tsx
index 07e2b5daf0..5328ed2732 100644
--- a/web/app/components/workflow/nodes/iteration/add-block.tsx
+++ b/web/app/components/workflow/nodes/iteration/add-block.tsx
@@ -49,9 +49,9 @@ const AddBlock = ({
const renderTriggerElement = useCallback((open: boolean) => {
return (
{t('workflow.common.addBlock')}
diff --git a/web/app/components/workflow/nodes/iteration/default.ts b/web/app/components/workflow/nodes/iteration/default.ts
index cdef268adb..0ef8382abe 100644
--- a/web/app/components/workflow/nodes/iteration/default.ts
+++ b/web/app/components/workflow/nodes/iteration/default.ts
@@ -4,7 +4,7 @@ import type { IterationNodeType } from './types'
import {
ALL_CHAT_AVAILABLE_BLOCKS,
ALL_COMPLETION_AVAILABLE_BLOCKS,
-} from '@/app/components/workflow/constants'
+} from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow'
const nodeDefault: NodeDefault
= {
diff --git a/web/app/components/workflow/nodes/iteration/node.tsx b/web/app/components/workflow/nodes/iteration/node.tsx
index fda033b87a..63874f7d6c 100644
--- a/web/app/components/workflow/nodes/iteration/node.tsx
+++ b/web/app/components/workflow/nodes/iteration/node.tsx
@@ -43,14 +43,14 @@ const Node: FC> = ({
return (
{
data._isCandidate && (
diff --git a/web/app/components/workflow/nodes/iteration/panel.tsx b/web/app/components/workflow/nodes/iteration/panel.tsx
index 9b6b3d3790..b21f11fc7a 100644
--- a/web/app/components/workflow/nodes/iteration/panel.tsx
+++ b/web/app/components/workflow/nodes/iteration/panel.tsx
@@ -76,7 +76,7 @@ const Panel: FC> = ({
Array
+ Array
)}
>
> = ({
Array
+
Array
)}
>
> = ({
-
+
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx
index 3e9be6485b..e65495d370 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-item.tsx
@@ -23,6 +23,7 @@ type Props = {
onRemove: () => void
onChange: (dataSet: DataSet) => void
readonly?: boolean
+ editable?: boolean
}
const DatasetItem: FC = ({
@@ -30,6 +31,7 @@ const DatasetItem: FC = ({
onRemove,
onChange,
readonly,
+ editable = true,
}) => {
const media = useBreakpoints()
const { t } = useTranslation()
@@ -75,14 +77,16 @@ const DatasetItem: FC = ({
{!readonly && (
-
{
- e.stopPropagation()
- showSettingsModal()
- }}
- >
-
-
+ {
+ editable &&
{
+ e.stopPropagation()
+ showSettingsModal()
+ }}
+ >
+
+
+ }
= ({
{
payload.provider === 'external' &&
}
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx
index 3a31cddbce..a30de8b104 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/dataset-list.tsx
@@ -1,10 +1,13 @@
'use client'
import type { FC } from 'react'
-import React, { useCallback } from 'react'
+import React, { useCallback, useMemo } from 'react'
import produce from 'immer'
import { useTranslation } from 'react-i18next'
import Item from './dataset-item'
import type { DataSet } from '@/models/datasets'
+import { useSelector as useAppContextSelector } from '@/context/app-context'
+import { hasEditPermissionForDataset } from '@/utils/permission'
+
type Props = {
list: DataSet[]
onChange: (list: DataSet[]) => void
@@ -17,6 +20,7 @@ const DatasetList: FC = ({
readonly,
}) => {
const { t } = useTranslation()
+ const userProfile = useAppContextSelector(s => s.userProfile)
const handleRemove = useCallback((index: number) => {
return () => {
@@ -35,10 +39,25 @@ const DatasetList: FC = ({
onChange(newList)
}
}, [list, onChange])
+
+ const formattedList = useMemo(() => {
+ return list.map((item) => {
+ const datasetConfig = {
+ createdBy: item.created_by,
+ partialMemberList: item.partial_member_list || [],
+ permission: item.permission,
+ }
+ return {
+ ...item,
+ editable: hasEditPermissionForDataset(userProfile?.id || '', datasetConfig),
+ }
+ })
+ }, [list, userProfile?.id])
+
return (
- {list.length
- ? list.map((item, index) => {
+ {formattedList.length
+ ? formattedList.map((item, index) => {
return (
- = ({
onRemove={handleRemove(index)}
onChange={handleChange(index)}
readonly={readonly}
+ editable={item.editable}
/>
)
})
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts
index e902d29b96..09da8dd789 100644
--- a/web/app/components/workflow/nodes/knowledge-retrieval/default.ts
+++ b/web/app/components/workflow/nodes/knowledge-retrieval/default.ts
@@ -2,7 +2,7 @@ import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import type { KnowledgeRetrievalNodeType } from './types'
import { checkoutRerankModelConfigedInRetrievalSettings } from './utils'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
import { DATASET_DEFAULT } from '@/config'
import { RETRIEVE_TYPE } from '@/types/app'
const i18nPrefix = 'workflow'
diff --git a/web/app/components/workflow/nodes/list-operator/default.ts b/web/app/components/workflow/nodes/list-operator/default.ts
index fe8773a914..0256cb8673 100644
--- a/web/app/components/workflow/nodes/list-operator/default.ts
+++ b/web/app/components/workflow/nodes/list-operator/default.ts
@@ -2,7 +2,7 @@ import { BlockEnum, VarType } 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'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/llm/default.ts b/web/app/components/workflow/nodes/llm/default.ts
index cddfafcb12..92377f74b8 100644
--- a/web/app/components/workflow/nodes/llm/default.ts
+++ b/web/app/components/workflow/nodes/llm/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum, EditionType } from '../../types'
import { type NodeDefault, type PromptItem, PromptRole } from '../../types'
import type { LLMNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
diff --git a/web/app/components/workflow/nodes/parameter-extractor/default.ts b/web/app/components/workflow/nodes/parameter-extractor/default.ts
index 69bb67eb9b..0e3b707d30 100644
--- a/web/app/components/workflow/nodes/parameter-extractor/default.ts
+++ b/web/app/components/workflow/nodes/parameter-extractor/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import { type ParameterExtractorNodeType, ReasoningModeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/question-classifier/default.ts b/web/app/components/workflow/nodes/question-classifier/default.ts
index b01db041da..2729c53f29 100644
--- a/web/app/components/workflow/nodes/question-classifier/default.ts
+++ b/web/app/components/workflow/nodes/question-classifier/default.ts
@@ -1,7 +1,7 @@
import type { NodeDefault } from '../../types'
import { BlockEnum } from '../../types'
import type { QuestionClassifierNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow'
diff --git a/web/app/components/workflow/nodes/start/default.ts b/web/app/components/workflow/nodes/start/default.ts
index a3c7ae1560..98f24c5d98 100644
--- a/web/app/components/workflow/nodes/start/default.ts
+++ b/web/app/components/workflow/nodes/start/default.ts
@@ -1,6 +1,6 @@
import type { NodeDefault } from '../../types'
import type { StartNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const nodeDefault: NodeDefault = {
defaultValue: {
diff --git a/web/app/components/workflow/nodes/template-transform/default.ts b/web/app/components/workflow/nodes/template-transform/default.ts
index 14dd6989ed..c698680342 100644
--- a/web/app/components/workflow/nodes/template-transform/default.ts
+++ b/web/app/components/workflow/nodes/template-transform/default.ts
@@ -1,7 +1,7 @@
import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import type { TemplateTransformNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault = {
diff --git a/web/app/components/workflow/nodes/tool/default.ts b/web/app/components/workflow/nodes/tool/default.ts
index 3b7f990a9f..f245929684 100644
--- a/web/app/components/workflow/nodes/tool/default.ts
+++ b/web/app/components/workflow/nodes/tool/default.ts
@@ -2,7 +2,7 @@ import { BlockEnum } from '../../types'
import type { NodeDefault } from '../../types'
import type { ToolNodeType } from './types'
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow.errorMsg'
diff --git a/web/app/components/workflow/nodes/variable-assigner/default.ts b/web/app/components/workflow/nodes/variable-assigner/default.ts
index b30e64961d..49e497e2c9 100644
--- a/web/app/components/workflow/nodes/variable-assigner/default.ts
+++ b/web/app/components/workflow/nodes/variable-assigner/default.ts
@@ -1,7 +1,7 @@
import { type NodeDefault, VarType } from '../../types'
import { BlockEnum } from '../../types'
import type { VariableAssignerNodeType } from './types'
-import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/constants'
+import { ALL_CHAT_AVAILABLE_BLOCKS, ALL_COMPLETION_AVAILABLE_BLOCKS } from '@/app/components/workflow/blocks'
const i18nPrefix = 'workflow'
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 42c30df7cf..9285516935 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
@@ -19,14 +19,14 @@ import ConversationVariableModal from './conversation-variable-modal'
import { useChat } from './hooks'
import type { ChatWrapperRefType } from './index'
import Chat from '@/app/components/base/chat/chat'
-import type { ChatItem, OnSend } from '@/app/components/base/chat/types'
+import type { ChatItem, ChatItemInTree, OnSend } from '@/app/components/base/chat/types'
import { useFeatures } from '@/app/components/base/features/hooks'
import {
fetchSuggestedQuestions,
stopChatMessageResponding,
} from '@/service/debug'
import { useStore as useAppStore } from '@/app/components/app/store'
-import { getLastAnswer } from '@/app/components/base/chat/utils'
+import { getLastAnswer, isValidGeneratedAnswer } from '@/app/components/base/chat/utils'
type ChatWrapperProps = {
showConversationVariableModal: boolean
@@ -65,13 +65,12 @@ const ChatWrapper = forwardRef(({
const {
conversationId,
chatList,
- chatListRef,
- handleUpdateChatList,
handleStop,
isResponding,
suggestedQuestions,
handleSend,
handleRestart,
+ setTargetMessageId,
} = useChat(
config,
{
@@ -82,36 +81,26 @@ const ChatWrapper = forwardRef(({
taskId => stopChatMessageResponding(appDetail!.id, taskId),
)
- const doSend = useCallback((query, files, last_answer) => {
+ const doSend: OnSend = useCallback((message, files, isRegenerate = false, parentAnswer: ChatItem | null = null) => {
handleSend(
{
- query,
+ query: message,
files,
inputs: workflowStore.getState().inputs,
conversation_id: conversationId,
- parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
+ parent_message_id: (isRegenerate ? parentAnswer?.id : getLastAnswer(chatList)?.id) || undefined,
},
{
onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
},
)
- }, [chatListRef, conversationId, handleSend, workflowStore, appDetail])
+ }, [handleSend, workflowStore, conversationId, chatList, appDetail])
- const doRegenerate = useCallback((chatItem: ChatItem) => {
- const index = chatList.findIndex(item => item.id === chatItem.id)
- if (index === -1)
- return
-
- const prevMessages = chatList.slice(0, index)
- const question = prevMessages.pop()
- const lastAnswer = getLastAnswer(prevMessages)
-
- if (!question)
- return
-
- handleUpdateChatList(prevMessages)
- doSend(question.content, question.message_files, lastAnswer)
- }, [chatList, handleUpdateChatList, doSend])
+ const doRegenerate = useCallback((chatItem: ChatItemInTree) => {
+ const question = chatList.find(item => item.id === chatItem.parentMessageId)!
+ const parentAnswer = chatList.find(item => item.id === question.parentMessageId)
+ doSend(question.content, question.message_files, true, isValidGeneratedAnswer(parentAnswer) ? parentAnswer : null)
+ }, [chatList, doSend])
useImperativeHandle(ref, () => {
return {
@@ -159,6 +148,7 @@ const ChatWrapper = forwardRef(({
suggestedQuestions={suggestedQuestions}
showPromptLog
chatAnswerContainerInner='!pr-2'
+ switchSibling={setTargetMessageId}
/>
{showConversationVariableModal && (
void
@@ -39,7 +42,7 @@ export const useChat = (
inputs: Inputs
inputsForm: InputForm[]
},
- prevChatList?: ChatItem[],
+ prevChatTree?: ChatItemInTree[],
stopChat?: (taskId: string) => void,
) => {
const { t } = useTranslation()
@@ -49,16 +52,54 @@ export const useChat = (
const workflowStore = useWorkflowStore()
const conversationId = useRef('')
const taskIdRef = useRef('')
- const [chatList, setChatList] = useState(prevChatList || [])
- const chatListRef = useRef(prevChatList || [])
const [isResponding, setIsResponding] = useState(false)
const isRespondingRef = useRef(false)
const [suggestedQuestions, setSuggestQuestions] = useState([])
const suggestedQuestionsAbortControllerRef = useRef(null)
-
const {
setIterTimes,
} = workflowStore.getState()
+
+ const handleResponding = useCallback((isResponding: boolean) => {
+ setIsResponding(isResponding)
+ isRespondingRef.current = isResponding
+ }, [])
+
+ const [chatTree, setChatTree] = useState(prevChatTree || [])
+ const chatTreeRef = useRef(chatTree)
+ const [targetMessageId, setTargetMessageId] = useState()
+ const threadMessages = useMemo(() => getThreadMessages(chatTree, targetMessageId), [chatTree, targetMessageId])
+
+ const getIntroduction = useCallback((str: string) => {
+ return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
+ }, [formSettings?.inputs, formSettings?.inputsForm])
+
+ /** Final chat list that will be rendered */
+ const chatList = useMemo(() => {
+ const ret = [...threadMessages]
+ if (config?.opening_statement) {
+ const index = threadMessages.findIndex(item => item.isOpeningStatement)
+
+ if (index > -1) {
+ ret[index] = {
+ ...ret[index],
+ content: getIntroduction(config.opening_statement),
+ suggestedQuestions: config.suggested_questions,
+ }
+ }
+ else {
+ ret.unshift({
+ id: `${Date.now()}`,
+ content: getIntroduction(config.opening_statement),
+ isAnswer: true,
+ isOpeningStatement: true,
+ suggestedQuestions: config.suggested_questions,
+ })
+ }
+ }
+ return ret
+ }, [threadMessages, config?.opening_statement, getIntroduction, config?.suggested_questions])
+
useEffect(() => {
setAutoFreeze(false)
return () => {
@@ -66,43 +107,21 @@ export const useChat = (
}
}, [])
- const handleUpdateChatList = useCallback((newChatList: ChatItem[]) => {
- setChatList(newChatList)
- chatListRef.current = newChatList
- }, [])
-
- const handleResponding = useCallback((isResponding: boolean) => {
- setIsResponding(isResponding)
- isRespondingRef.current = isResponding
- }, [])
-
- const getIntroduction = useCallback((str: string) => {
- return processOpeningStatement(str, formSettings?.inputs || {}, formSettings?.inputsForm || [])
- }, [formSettings?.inputs, formSettings?.inputsForm])
- useEffect(() => {
- if (config?.opening_statement) {
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const index = draft.findIndex(item => item.isOpeningStatement)
-
- if (index > -1) {
- draft[index] = {
- ...draft[index],
- content: getIntroduction(config.opening_statement),
- suggestedQuestions: config.suggested_questions,
- }
+ /** Find the target node by bfs and then operate on it */
+ const produceChatTreeNode = useCallback((targetId: string, operation: (node: ChatItemInTree) => void) => {
+ return produce(chatTreeRef.current, (draft) => {
+ const queue: ChatItemInTree[] = [...draft]
+ while (queue.length > 0) {
+ const current = queue.shift()!
+ if (current.id === targetId) {
+ operation(current)
+ break
}
- else {
- draft.unshift({
- id: `${Date.now()}`,
- content: getIntroduction(config.opening_statement),
- isAnswer: true,
- isOpeningStatement: true,
- suggestedQuestions: config.suggested_questions,
- })
- }
- }))
- }
- }, [config?.opening_statement, getIntroduction, config?.suggested_questions, handleUpdateChatList])
+ if (current.children)
+ queue.push(...current.children)
+ }
+ })
+ }, [])
const handleStop = useCallback(() => {
hasStopResponded.current = true
@@ -119,50 +138,52 @@ export const useChat = (
taskIdRef.current = ''
handleStop()
setIterTimes(DEFAULT_ITER_TIMES)
- const newChatList = config?.opening_statement
- ? [{
- id: `${Date.now()}`,
- content: config.opening_statement,
- isAnswer: true,
- isOpeningStatement: true,
- suggestedQuestions: config.suggested_questions,
- }]
- : []
- handleUpdateChatList(newChatList)
+ setChatTree([])
setSuggestQuestions([])
}, [
- config,
handleStop,
- handleUpdateChatList,
setIterTimes,
])
- const updateCurrentQA = useCallback(({
+ const updateCurrentQAOnTree = useCallback(({
+ parentId,
responseItem,
- questionId,
- placeholderAnswerId,
+ placeholderQuestionId,
questionItem,
}: {
+ parentId?: string
responseItem: ChatItem
- questionId: string
- placeholderAnswerId: string
+ placeholderQuestionId: string
questionItem: ChatItem
}) => {
- const newListWithAnswer = produce(
- chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
- (draft) => {
- if (!draft.find(item => item.id === questionId))
- draft.push({ ...questionItem })
-
- draft.push({ ...responseItem })
+ let nextState: ChatItemInTree[]
+ const currentQA = { ...questionItem, children: [{ ...responseItem, children: [] }] }
+ if (!parentId && !chatTree.some(item => [placeholderQuestionId, questionItem.id].includes(item.id))) {
+ // QA whose parent is not provided is considered as a first message of the conversation,
+ // and it should be a root node of the chat tree
+ nextState = produce(chatTree, (draft) => {
+ draft.push(currentQA)
})
- handleUpdateChatList(newListWithAnswer)
- }, [handleUpdateChatList])
+ }
+ else {
+ // find the target QA in the tree and update it; if not found, insert it to its parent node
+ nextState = produceChatTreeNode(parentId!, (parentNode) => {
+ const questionNodeIndex = parentNode.children!.findIndex(item => [placeholderQuestionId, questionItem.id].includes(item.id))
+ if (questionNodeIndex === -1)
+ parentNode.children!.push(currentQA)
+ else
+ parentNode.children![questionNodeIndex] = currentQA
+ })
+ }
+ setChatTree(nextState)
+ chatTreeRef.current = nextState
+ }, [chatTree, produceChatTreeNode])
const handleSend = useCallback((
params: {
query: string
files?: FileEntity[]
+ parent_message_id?: string
[key: string]: any
},
{
@@ -174,12 +195,15 @@ export const useChat = (
return false
}
- const questionId = `question-${Date.now()}`
+ const parentMessage = threadMessages.find(item => item.id === params.parent_message_id)
+
+ const placeholderQuestionId = `question-${Date.now()}`
const questionItem = {
- id: questionId,
+ id: placeholderQuestionId,
content: params.query,
isAnswer: false,
message_files: params.files,
+ parentMessageId: params.parent_message_id,
}
const placeholderAnswerId = `answer-placeholder-${Date.now()}`
@@ -187,10 +211,17 @@ export const useChat = (
id: placeholderAnswerId,
content: '',
isAnswer: true,
+ parentMessageId: questionItem.id,
+ siblingIndex: parentMessage?.children?.length ?? chatTree.length,
}
- const newList = [...chatListRef.current, questionItem, placeholderAnswerItem]
- handleUpdateChatList(newList)
+ setTargetMessageId(parentMessage?.id)
+ updateCurrentQAOnTree({
+ parentId: params.parent_message_id,
+ responseItem: placeholderAnswerItem,
+ placeholderQuestionId,
+ questionItem,
+ })
// answer
const responseItem: ChatItem = {
@@ -199,6 +230,8 @@ export const useChat = (
agent_thoughts: [],
message_files: [],
isAnswer: true,
+ parentMessageId: questionItem.id,
+ siblingIndex: parentMessage?.children?.length ?? chatTree.length,
}
handleResponding(true)
@@ -230,7 +263,9 @@ export const useChat = (
responseItem.content = responseItem.content + message
if (messageId && !hasSetResponseId) {
+ questionItem.id = `question-${messageId}`
responseItem.id = messageId
+ responseItem.parentMessageId = questionItem.id
hasSetResponseId = true
}
@@ -241,11 +276,11 @@ export const useChat = (
if (messageId)
responseItem.id = messageId
- updateCurrentQA({
- responseItem,
- questionId,
- placeholderAnswerId,
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
})
},
async onCompleted(hasError?: boolean, errorMessage?: string) {
@@ -255,15 +290,12 @@ export const useChat = (
if (errorMessage) {
responseItem.content = errorMessage
responseItem.isError = true
- const newListWithAnswer = produce(
- chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
- (draft) => {
- if (!draft.find(item => item.id === questionId))
- draft.push({ ...questionItem })
-
- draft.push({ ...responseItem })
- })
- handleUpdateChatList(newListWithAnswer)
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
}
return
}
@@ -286,15 +318,12 @@ export const useChat = (
const processedFilesFromResponse = getProcessedFilesFromResponse(messageEnd.files || [])
responseItem.allFiles = uniqBy([...(responseItem.allFiles || []), ...(processedFilesFromResponse || [])], 'id')
- const newListWithAnswer = produce(
- chatListRef.current.filter(item => item.id !== responseItem.id && item.id !== placeholderAnswerId),
- (draft) => {
- if (!draft.find(item => item.id === questionId))
- draft.push({ ...questionItem })
-
- draft.push({ ...responseItem })
- })
- handleUpdateChatList(newListWithAnswer)
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onMessageReplace: (messageReplace) => {
responseItem.content = messageReplace.answer
@@ -309,23 +338,21 @@ export const useChat = (
status: WorkflowRunningStatus.Running,
tracing: [],
}
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
- draft[currentIndex] = {
- ...draft[currentIndex],
- ...responseItem,
- }
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onWorkflowFinished: ({ data }) => {
responseItem.workflowProcess!.status = data.status as WorkflowRunningStatus
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
- draft[currentIndex] = {
- ...draft[currentIndex],
- ...responseItem,
- }
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onIterationStart: ({ data }) => {
responseItem.workflowProcess!.tracing!.push({
@@ -333,13 +360,12 @@ export const useChat = (
status: NodeRunningStatus.Running,
details: [],
} as any)
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
- draft[currentIndex] = {
- ...draft[currentIndex],
- ...responseItem,
- }
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onIterationNext: ({ data }) => {
const tracing = responseItem.workflowProcess!.tracing!
@@ -347,10 +373,12 @@ export const useChat = (
&& (item.execution_metadata?.parallel_id === data.execution_metadata?.parallel_id || item.parallel_id === data.execution_metadata?.parallel_id))!
iterations.details!.push([])
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.length - 1
- draft[currentIndex] = responseItem
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onIterationFinish: ({ data }) => {
const tracing = responseItem.workflowProcess!.tracing!
@@ -361,10 +389,12 @@ export const useChat = (
...data,
status: NodeRunningStatus.Succeeded,
} as any
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.length - 1
- draft[currentIndex] = responseItem
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onNodeStarted: ({ data }) => {
if (data.iteration_id)
@@ -374,13 +404,12 @@ export const useChat = (
...data,
status: NodeRunningStatus.Running,
} as any)
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
- draft[currentIndex] = {
- ...draft[currentIndex],
- ...responseItem,
- }
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
onNodeRetry: ({ data }) => {
if (data.iteration_id)
@@ -422,23 +451,21 @@ export const useChat = (
: {}),
...data,
} as any
- handleUpdateChatList(produce(chatListRef.current, (draft) => {
- const currentIndex = draft.findIndex(item => item.id === responseItem.id)
- draft[currentIndex] = {
- ...draft[currentIndex],
- ...responseItem,
- }
- }))
+ updateCurrentQAOnTree({
+ placeholderQuestionId,
+ questionItem,
+ responseItem,
+ parentId: params.parent_message_id,
+ })
},
},
)
- }, [handleRun, handleResponding, handleUpdateChatList, notify, t, updateCurrentQA, config.suggested_questions_after_answer?.enabled, formSettings])
+ }, [threadMessages, chatTree.length, updateCurrentQAOnTree, handleResponding, formSettings?.inputsForm, handleRun, notify, t, config?.suggested_questions_after_answer?.enabled])
return {
conversationId: conversationId.current,
chatList,
- chatListRef,
- handleUpdateChatList,
+ setTargetMessageId,
handleSend,
handleStop,
handleRestart,
diff --git a/web/app/components/workflow/run/output-panel.tsx b/web/app/components/workflow/run/output-panel.tsx
index a1667d9b45..ee508572c5 100644
--- a/web/app/components/workflow/run/output-panel.tsx
+++ b/web/app/components/workflow/run/output-panel.tsx
@@ -23,7 +23,14 @@ const OutputPanel: FC = ({
height,
}) => {
const isTextOutput = useMemo(() => {
- return outputs && Object.keys(outputs).length === 1 && typeof outputs[Object.keys(outputs)[0]] === 'string'
+ if (!outputs || typeof outputs !== 'object')
+ return false
+ const keys = Object.keys(outputs)
+ const value = outputs[keys[0]]
+ return keys.length === 1 && (
+ typeof value === 'string'
+ || (Array.isArray(value) && value.every(item => typeof item === 'string'))
+ )
}, [outputs])
const fileList = useMemo(() => {
@@ -65,7 +72,13 @@ const OutputPanel: FC = ({
)}
{isTextOutput && (
-
+
)}
{fileList.length > 0 && (
@@ -78,14 +91,14 @@ const OutputPanel: FC = ({
/>
)}
- {outputs && Object.keys(outputs).length > 1 && height! > 0 && (
+ {!isTextOutput && outputs && Object.keys(outputs).length > 0 && height! > 0 && (
}
+ title={Output
}
language={CodeLanguage.json}
- value={outputs}
+ value={JSON.stringify(outputs, null, 2)}
isJSONStringifyBeauty
height={height ? (height - 16) / 2 : undefined}
/>
diff --git a/web/app/styles/markdown.scss b/web/app/styles/markdown.scss
index 214d8d2782..92331505ec 100644
--- a/web/app/styles/markdown.scss
+++ b/web/app/styles/markdown.scss
@@ -47,7 +47,7 @@
.markdown-body {
-ms-text-size-adjust: 100%;
-webkit-text-size-adjust: 100%;
- margin: 0;
+ margin: 4px 0 0 0;
color: #101828;
background-color: var(--color-canvas-default);
font-size: 14px;
diff --git a/web/i18n/zh-Hant/workflow.ts b/web/i18n/zh-Hant/workflow.ts
index 54599d23ff..1bfc258317 100644
--- a/web/i18n/zh-Hant/workflow.ts
+++ b/web/i18n/zh-Hant/workflow.ts
@@ -335,7 +335,7 @@ const translation = {
retryFailed: '重試失敗',
retryFailedTimes: '{{times}} 次重試失敗',
times: '次',
- ms: '女士',
+ ms: '毫秒',
retries: '{{num}}重試',
},
},
diff --git a/web/models/datasets.ts b/web/models/datasets.ts
index 673fb5fb15..170fe1911f 100644
--- a/web/models/datasets.ts
+++ b/web/models/datasets.ts
@@ -9,7 +9,11 @@ export enum DataSourceType {
WEB = 'website_crawl',
}
-export type DatasetPermission = 'only_me' | 'all_team_members' | 'partial_members'
+export enum DatasetPermission {
+ 'onlyMe' = 'only_me',
+ 'allTeamMembers' = 'all_team_members',
+ 'partialMembers' = 'partial_members',
+}
export enum ChunkingMode {
'text' = 'text_model', // General text
@@ -40,7 +44,7 @@ export type DataSet = {
retrieval_model_dict: RetrievalConfig
retrieval_model: RetrievalConfig
tags: Tag[]
- partial_member_list?: any[]
+ partial_member_list?: string[]
external_knowledge_info: {
external_knowledge_id: string
external_knowledge_api_id: string
diff --git a/web/package.json b/web/package.json
index 6ae11d71b4..952fac4a5f 100644
--- a/web/package.json
+++ b/web/package.json
@@ -62,7 +62,7 @@
"js-audio-recorder": "^1.0.7",
"js-cookie": "^3.0.1",
"jwt-decode": "^4.0.0",
- "katex": "^0.16.10",
+ "katex": "^0.16.21",
"lamejs": "^1.2.1",
"lexical": "^0.16.0",
"line-clamp": "^1.0.0",
diff --git a/web/utils/permission.ts b/web/utils/permission.ts
new file mode 100644
index 0000000000..8a7b9f5fa1
--- /dev/null
+++ b/web/utils/permission.ts
@@ -0,0 +1,18 @@
+import { DatasetPermission } from '@/models/datasets'
+
+type DatasetConfig = {
+ createdBy: string
+ partialMemberList: string[]
+ permission: DatasetPermission
+}
+
+export const hasEditPermissionForDataset = (userId: string, datasetConfig: DatasetConfig) => {
+ const { createdBy, partialMemberList, permission } = datasetConfig
+ if (permission === DatasetPermission.onlyMe)
+ return userId === createdBy
+ if (permission === DatasetPermission.allTeamMembers)
+ return true
+ if (permission === DatasetPermission.partialMembers)
+ return partialMemberList.includes(userId)
+ return false
+}
diff --git a/web/yarn.lock b/web/yarn.lock
index 6eed53dd39..9e64831e3e 100644
--- a/web/yarn.lock
+++ b/web/yarn.lock
@@ -5563,12 +5563,7 @@ debug@^4.4.0:
dependencies:
ms "^2.1.3"
-decimal.js@^10.4.2:
- version "10.4.3"
- resolved "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz"
- integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
-
-decimal.js@^10.4.3:
+decimal.js@^10.4.2, decimal.js@^10.4.3:
version "10.4.3"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.4.3.tgz#1044092884d245d1b7f65725fa4ad4c6f781cc23"
integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==
@@ -8641,10 +8636,10 @@ jwt-decode@^4.0.0:
resolved "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz"
integrity sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==
-katex@^0.16.0, katex@^0.16.10, katex@^0.16.9:
- version "0.16.10"
- resolved "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz"
- integrity sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==
+katex@^0.16.0, katex@^0.16.21, katex@^0.16.9:
+ version "0.16.21"
+ resolved "https://registry.yarnpkg.com/katex/-/katex-0.16.21.tgz#8f63c659e931b210139691f2cc7bb35166b792a3"
+ integrity sha512-XvqR7FgOHtWupfMiigNzmh+MgUVmDGU2kXZm899ZkPfcuoPuFxyHmXsgATDpFZDAXCI8tvinaVcDo8PIIJSo4A==
dependencies:
commander "^8.3.0"