mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-18 05:45:59 +08:00
webapp chat embedded chat
This commit is contained in:
parent
296253a365
commit
a7d53abba9
@ -3,17 +3,20 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useChatWithHistoryContext } from '../context'
|
import { useChatWithHistoryContext } from '../context'
|
||||||
import Input from './form-input'
|
import Input from './form-input'
|
||||||
import { PortalSelect } from '@/app/components/base/select'
|
import { PortalSelect } from '@/app/components/base/select'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
appParams,
|
||||||
inputsForms,
|
inputsForms,
|
||||||
newConversationInputs,
|
newConversationInputs,
|
||||||
handleNewConversationInputsChange,
|
handleNewConversationInputsChange,
|
||||||
isMobile,
|
isMobile,
|
||||||
} = useChatWithHistoryContext()
|
} = useChatWithHistoryContext()
|
||||||
|
|
||||||
const handleFormChange = useCallback((variable: string, value: string) => {
|
const handleFormChange = useCallback((variable: string, value: any) => {
|
||||||
handleNewConversationInputsChange({
|
handleNewConversationInputsChange({
|
||||||
...newConversationInputs,
|
...newConversationInputs,
|
||||||
[variable]: value,
|
[variable]: value,
|
||||||
@ -48,6 +51,20 @@ const Form = () => {
|
|||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (form.type === InputVarType.multiFiles) {
|
||||||
|
return (
|
||||||
|
<FileUploaderInAttachmentWrapper
|
||||||
|
value={newConversationInputs[variable]}
|
||||||
|
onChange={files => handleFormChange(variable, files)}
|
||||||
|
fileConfig={{
|
||||||
|
allowed_file_types: appParams?.file_upload?.allowed_file_types,
|
||||||
|
allowed_file_extensions: appParams?.file_upload?.allowed_file_extensions,
|
||||||
|
allowed_file_upload_methods: appParams?.file_upload?.allowed_file_upload_methods,
|
||||||
|
number_limits: appParams?.file_upload?.number_limits,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalSelect
|
<PortalSelect
|
||||||
|
@ -127,7 +127,7 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
|||||||
setNewConversationInputs(newInputs)
|
setNewConversationInputs(newInputs)
|
||||||
}, [])
|
}, [])
|
||||||
const inputsForms = useMemo(() => {
|
const inputsForms = useMemo(() => {
|
||||||
return (appParams?.user_input_form || []).filter((item: any) => item.paragraph || item.select || item['text-input'] || item.number).map((item: any) => {
|
return (appParams?.user_input_form || []).filter((item: any) => !item.external_data_tool).map((item: any) => {
|
||||||
if (item.paragraph) {
|
if (item.paragraph) {
|
||||||
return {
|
return {
|
||||||
...item.paragraph,
|
...item.paragraph,
|
||||||
@ -147,6 +147,20 @@ export const useChatWithHistory = (installedAppInfo?: InstalledApp) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item['file-list']) {
|
||||||
|
return {
|
||||||
|
...item['file-list'],
|
||||||
|
type: 'file-list',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.file) {
|
||||||
|
return {
|
||||||
|
...item.file,
|
||||||
|
type: 'file',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...item['text-input'],
|
...item['text-input'],
|
||||||
type: 'text-input',
|
type: 'text-input',
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
} from 'react'
|
} from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { produce, setAutoFreeze } from 'immer'
|
import { produce, setAutoFreeze } from 'immer'
|
||||||
|
import { uniqBy } from 'lodash-es'
|
||||||
import { useParams, usePathname } from 'next/navigation'
|
import { useParams, usePathname } from 'next/navigation'
|
||||||
import { v4 as uuidV4 } from 'uuid'
|
import { v4 as uuidV4 } from 'uuid'
|
||||||
import type {
|
import type {
|
||||||
@ -14,7 +15,10 @@ import type {
|
|||||||
Inputs,
|
Inputs,
|
||||||
} from '../types'
|
} from '../types'
|
||||||
import type { InputForm } from './type'
|
import type { InputForm } from './type'
|
||||||
import { processOpeningStatement } from './utils'
|
import {
|
||||||
|
getProcessedInputs,
|
||||||
|
processOpeningStatement,
|
||||||
|
} from './utils'
|
||||||
import { TransferMethod } from '@/types/app'
|
import { TransferMethod } from '@/types/app'
|
||||||
import { useToastContext } from '@/app/components/base/toast'
|
import { useToastContext } from '@/app/components/base/toast'
|
||||||
import { ssePost } from '@/service/base'
|
import { ssePost } from '@/service/base'
|
||||||
@ -23,7 +27,10 @@ import { WorkflowRunningStatus } from '@/app/components/workflow/types'
|
|||||||
import useTimestamp from '@/hooks/use-timestamp'
|
import useTimestamp from '@/hooks/use-timestamp'
|
||||||
import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
|
import { AudioPlayerManager } from '@/app/components/base/audio-btn/audio.player.manager'
|
||||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||||
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
import {
|
||||||
|
getProcessedFiles,
|
||||||
|
getProcessedFilesFromResponse,
|
||||||
|
} from '@/app/components/base/file-uploader/utils'
|
||||||
|
|
||||||
type GetAbortController = (abortController: AbortController) => void
|
type GetAbortController = (abortController: AbortController) => void
|
||||||
type SendCallback = {
|
type SendCallback = {
|
||||||
@ -206,12 +213,13 @@ export const useChat = (
|
|||||||
handleResponding(true)
|
handleResponding(true)
|
||||||
hasStopResponded.current = false
|
hasStopResponded.current = false
|
||||||
|
|
||||||
const { query, files, ...restData } = data
|
const { query, files, inputs, ...restData } = data
|
||||||
const bodyParams = {
|
const bodyParams = {
|
||||||
response_mode: 'streaming',
|
response_mode: 'streaming',
|
||||||
conversation_id: conversationId.current,
|
conversation_id: conversationId.current,
|
||||||
files: getProcessedFiles(files || []),
|
files: getProcessedFiles(files || []),
|
||||||
query,
|
query,
|
||||||
|
inputs: getProcessedInputs(inputs || {}, formSettings?.inputsForm || []),
|
||||||
...restData,
|
...restData,
|
||||||
}
|
}
|
||||||
if (bodyParams?.files?.length) {
|
if (bodyParams?.files?.length) {
|
||||||
@ -512,6 +520,8 @@ export const useChat = (
|
|||||||
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata.parallel_id)
|
return item.node_id === data.node_id && (item.execution_metadata?.parallel_id === data.execution_metadata.parallel_id)
|
||||||
})
|
})
|
||||||
responseItem.workflowProcess!.tracing[currentIndex] = data as any
|
responseItem.workflowProcess!.tracing[currentIndex] = data as any
|
||||||
|
const processedFilesFromResponse = getProcessedFilesFromResponse(data.files || [])
|
||||||
|
responseItem.allFiles = uniqBy([...(responseItem.allFiles || []), ...(processedFilesFromResponse || [])], 'id')
|
||||||
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
handleUpdateChatList(produce(chatListRef.current, (draft) => {
|
||||||
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
const currentIndex = draft.findIndex(item => item.id === responseItem.id)
|
||||||
draft[currentIndex] = {
|
draft[currentIndex] = {
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import type { InputForm } from './type'
|
import type { InputForm } from './type'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
||||||
|
|
||||||
export const processOpeningStatement = (openingStatement: string, inputs: Record<string, any>, inputsForm: InputForm[]) => {
|
export const processOpeningStatement = (openingStatement: string, inputs: Record<string, any>, inputsForm: InputForm[]) => {
|
||||||
if (!openingStatement)
|
if (!openingStatement)
|
||||||
@ -14,3 +16,17 @@ export const processOpeningStatement = (openingStatement: string, inputs: Record
|
|||||||
return valueObj ? `{{${valueObj.label}}}` : match
|
return valueObj ? `{{${valueObj.label}}}` : match
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getProcessedInputs = (inputs: Record<string, any>, inputsForm: InputForm[]) => {
|
||||||
|
const processedInputs = { ...inputs }
|
||||||
|
|
||||||
|
inputsForm.forEach((item) => {
|
||||||
|
if (item.type === InputVarType.multiFiles && inputs[item.variable])
|
||||||
|
processedInputs[item.variable] = getProcessedFiles(inputs[item.variable])
|
||||||
|
|
||||||
|
if (item.type === InputVarType.singleFile && inputs[item.variable])
|
||||||
|
processedInputs[item.variable] = getProcessedFiles([inputs[item.variable]])[0]
|
||||||
|
})
|
||||||
|
|
||||||
|
return processedInputs
|
||||||
|
}
|
||||||
|
@ -58,7 +58,7 @@ const ChatWrapper = () => {
|
|||||||
appConfig,
|
appConfig,
|
||||||
{
|
{
|
||||||
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
|
inputs: (currentConversationId ? currentConversationItem?.inputs : newConversationInputs) as any,
|
||||||
promptVariables: inputsForms,
|
inputsForm: inputsForms,
|
||||||
},
|
},
|
||||||
appPrevChatList,
|
appPrevChatList,
|
||||||
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
|
taskId => stopChatMessageResponding('', taskId, isInstalledApp, appId),
|
||||||
@ -159,6 +159,8 @@ const ChatWrapper = () => {
|
|||||||
chatFooterClassName='pb-4'
|
chatFooterClassName='pb-4'
|
||||||
chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}
|
chatFooterInnerClassName={cn('mx-auto w-full max-w-full tablet:px-4', isMobile && 'px-4')}
|
||||||
onSend={doSend}
|
onSend={doSend}
|
||||||
|
inputs={currentConversationId ? currentConversationItem?.inputs as any : newConversationInputs}
|
||||||
|
inputsForm={inputsForms}
|
||||||
onRegenerate={doRegenerate}
|
onRegenerate={doRegenerate}
|
||||||
onStopResponding={handleStop}
|
onStopResponding={handleStop}
|
||||||
chatNode={chatNode}
|
chatNode={chatNode}
|
||||||
|
@ -3,17 +3,20 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { useEmbeddedChatbotContext } from '../context'
|
import { useEmbeddedChatbotContext } from '../context'
|
||||||
import Input from './form-input'
|
import Input from './form-input'
|
||||||
import { PortalSelect } from '@/app/components/base/select'
|
import { PortalSelect } from '@/app/components/base/select'
|
||||||
|
import { InputVarType } from '@/app/components/workflow/types'
|
||||||
|
import { FileUploaderInAttachmentWrapper } from '@/app/components/base/file-uploader'
|
||||||
|
|
||||||
const Form = () => {
|
const Form = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const {
|
const {
|
||||||
|
appParams,
|
||||||
inputsForms,
|
inputsForms,
|
||||||
newConversationInputs,
|
newConversationInputs,
|
||||||
handleNewConversationInputsChange,
|
handleNewConversationInputsChange,
|
||||||
isMobile,
|
isMobile,
|
||||||
} = useEmbeddedChatbotContext()
|
} = useEmbeddedChatbotContext()
|
||||||
|
|
||||||
const handleFormChange = useCallback((variable: string, value: string) => {
|
const handleFormChange = useCallback((variable: string, value: any) => {
|
||||||
handleNewConversationInputsChange({
|
handleNewConversationInputsChange({
|
||||||
...newConversationInputs,
|
...newConversationInputs,
|
||||||
[variable]: value,
|
[variable]: value,
|
||||||
@ -49,6 +52,32 @@ const Form = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (form.type === 'number') {
|
||||||
|
return (
|
||||||
|
<input
|
||||||
|
className="grow h-9 rounded-lg bg-gray-100 px-2.5 outline-none appearance-none"
|
||||||
|
type="number"
|
||||||
|
value={newConversationInputs[variable] || ''}
|
||||||
|
onChange={e => handleFormChange(variable, e.target.value)}
|
||||||
|
placeholder={`${label}${!required ? `(${t('appDebug.variableTable.optional')})` : ''}`}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (form.type === InputVarType.multiFiles) {
|
||||||
|
return (
|
||||||
|
<FileUploaderInAttachmentWrapper
|
||||||
|
value={newConversationInputs[variable]}
|
||||||
|
onChange={files => handleFormChange(variable, files)}
|
||||||
|
fileConfig={{
|
||||||
|
allowed_file_types: appParams?.file_upload?.allowed_file_types,
|
||||||
|
allowed_file_extensions: appParams?.file_upload?.allowed_file_extensions,
|
||||||
|
allowed_file_upload_methods: appParams?.file_upload?.allowed_file_upload_methods,
|
||||||
|
number_limits: appParams?.file_upload?.number_limits,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PortalSelect
|
<PortalSelect
|
||||||
popupClassName='w-[200px]'
|
popupClassName='w-[200px]'
|
||||||
|
@ -123,6 +123,20 @@ export const useEmbeddedChatbot = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (item['file-list']) {
|
||||||
|
return {
|
||||||
|
...item['file-list'],
|
||||||
|
type: 'file-list',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.file) {
|
||||||
|
return {
|
||||||
|
...item.file,
|
||||||
|
type: 'file',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let value = initInputs[item['text-input'].variable]
|
let value = initInputs[item['text-input'].variable]
|
||||||
if (value && item['text-input'].max_length && value.length > item['text-input'].max_length)
|
if (value && item['text-input'].max_length && value.length > item['text-input'].max_length)
|
||||||
value = value.slice(0, item['text-input'].max_length)
|
value = value.slice(0, item['text-input'].max_length)
|
||||||
|
@ -16,4 +16,3 @@ export * from './use-workflow-variables'
|
|||||||
export * from './use-shortcuts'
|
export * from './use-shortcuts'
|
||||||
export * from './use-workflow-interactions'
|
export * from './use-workflow-interactions'
|
||||||
export * from './use-workflow-mode'
|
export * from './use-workflow-mode'
|
||||||
export * from './use-check-start-node-form'
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
import { useCallback } from 'react'
|
|
||||||
import { useStoreApi } from 'reactflow'
|
|
||||||
import {
|
|
||||||
BlockEnum,
|
|
||||||
InputVarType,
|
|
||||||
} from '@/app/components/workflow/types'
|
|
||||||
import type { InputVar } from '@/app/components/workflow/types'
|
|
||||||
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
|
||||||
|
|
||||||
export const useCheckStartNodeForm = () => {
|
|
||||||
const storeApi = useStoreApi()
|
|
||||||
|
|
||||||
const getProcessedInputs = useCallback((inputs: Record<string, any>) => {
|
|
||||||
const { getNodes } = storeApi.getState()
|
|
||||||
const nodes = getNodes()
|
|
||||||
const startNode = nodes.find(node => node.data.type === BlockEnum.Start)
|
|
||||||
const variables: InputVar[] = startNode?.data.variables || []
|
|
||||||
|
|
||||||
const processedInputs = { ...inputs }
|
|
||||||
|
|
||||||
variables.forEach((variable) => {
|
|
||||||
if (variable.type === InputVarType.multiFiles && inputs[variable.variable])
|
|
||||||
processedInputs[variable.variable] = getProcessedFiles(inputs[variable.variable])
|
|
||||||
|
|
||||||
if (variable.type === InputVarType.singleFile && inputs[variable.variable])
|
|
||||||
processedInputs[variable.variable] = getProcessedFiles([inputs[variable.variable]])[0]
|
|
||||||
})
|
|
||||||
|
|
||||||
return processedInputs
|
|
||||||
}, [storeApi])
|
|
||||||
|
|
||||||
return {
|
|
||||||
getProcessedInputs,
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,6 @@ import {
|
|||||||
useStore,
|
useStore,
|
||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from '../../store'
|
} from '../../store'
|
||||||
import { useCheckStartNodeForm } from '../../hooks'
|
|
||||||
import type { StartNodeType } from '../../nodes/start/types'
|
import type { StartNodeType } from '../../nodes/start/types'
|
||||||
import Empty from './empty'
|
import Empty from './empty'
|
||||||
import UserInput from './user-input'
|
import UserInput from './user-input'
|
||||||
@ -62,7 +61,6 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
}
|
}
|
||||||
}, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
|
}, [features.opening, features.suggested, features.text2speech, features.speech2text, features.citation, features.moderation, features.file])
|
||||||
const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
|
const setShowFeaturesPanel = useStore(s => s.setShowFeaturesPanel)
|
||||||
const { getProcessedInputs } = useCheckStartNodeForm()
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
conversationId,
|
conversationId,
|
||||||
@ -89,7 +87,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
{
|
{
|
||||||
query,
|
query,
|
||||||
files,
|
files,
|
||||||
inputs: getProcessedInputs(workflowStore.getState().inputs),
|
inputs: workflowStore.getState().inputs,
|
||||||
conversation_id: conversationId,
|
conversation_id: conversationId,
|
||||||
parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
|
parent_message_id: last_answer?.id || getLastAnswer(chatListRef.current)?.id || null,
|
||||||
},
|
},
|
||||||
@ -97,7 +95,7 @@ const ChatWrapper = forwardRef<ChatWrapperRefType, ChatWrapperProps>(({
|
|||||||
onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
|
onGetSuggestedQuestions: (messageId, getAbortController) => fetchSuggestedQuestions(appDetail!.id, messageId, getAbortController),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}, [chatListRef, conversationId, handleSend, workflowStore, appDetail, getProcessedInputs])
|
}, [chatListRef, conversationId, handleSend, workflowStore, appDetail])
|
||||||
|
|
||||||
const doRegenerate = useCallback((chatItem: ChatItem) => {
|
const doRegenerate = useCallback((chatItem: ChatItem) => {
|
||||||
const index = chatList.findIndex(item => item.id === chatItem.id)
|
const index = chatList.findIndex(item => item.id === chatItem.id)
|
||||||
|
@ -15,7 +15,7 @@ import {
|
|||||||
useStore,
|
useStore,
|
||||||
useWorkflowStore,
|
useWorkflowStore,
|
||||||
} from '../store'
|
} from '../store'
|
||||||
import { useCheckStartNodeForm, useWorkflowRun } from '../hooks'
|
import { useWorkflowRun } from '../hooks'
|
||||||
import type { StartNodeType } from '../nodes/start/types'
|
import type { StartNodeType } from '../nodes/start/types'
|
||||||
import { TransferMethod } from '../../base/text-generation/types'
|
import { TransferMethod } from '../../base/text-generation/types'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
@ -56,8 +56,6 @@ const InputsPanel = ({ onRun }: Props) => {
|
|||||||
return data
|
return data
|
||||||
}, [fileSettings?.image?.enabled, startVariables])
|
}, [fileSettings?.image?.enabled, startVariables])
|
||||||
|
|
||||||
const { getProcessedInputs } = useCheckStartNodeForm()
|
|
||||||
|
|
||||||
const handleValueChange = (variable: string, v: any) => {
|
const handleValueChange = (variable: string, v: any) => {
|
||||||
const {
|
const {
|
||||||
inputs,
|
inputs,
|
||||||
@ -78,8 +76,8 @@ const InputsPanel = ({ onRun }: Props) => {
|
|||||||
|
|
||||||
const doRun = useCallback(() => {
|
const doRun = useCallback(() => {
|
||||||
onRun()
|
onRun()
|
||||||
handleRun({ inputs: getProcessedInputs(inputs), files })
|
handleRun({ inputs, files })
|
||||||
}, [files, getProcessedInputs, handleRun, inputs, onRun])
|
}, [files, handleRun, inputs, onRun])
|
||||||
|
|
||||||
const canRun = useMemo(() => {
|
const canRun = useMemo(() => {
|
||||||
if (files?.some(item => (item.transfer_method as any) === TransferMethod.local_file && !item.upload_file_id))
|
if (files?.some(item => (item.transfer_method as any) === TransferMethod.local_file && !item.upload_file_id))
|
||||||
|
@ -368,6 +368,7 @@ export type UploadFileSetting = {
|
|||||||
allowed_file_types: SupportUploadFileTypes[]
|
allowed_file_types: SupportUploadFileTypes[]
|
||||||
allowed_file_extensions?: string[]
|
allowed_file_extensions?: string[]
|
||||||
max_length: number
|
max_length: number
|
||||||
|
number_limits?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type VisionSetting = {
|
export type VisionSetting = {
|
||||||
|
@ -6,6 +6,7 @@ import type {
|
|||||||
RerankingModeEnum,
|
RerankingModeEnum,
|
||||||
WeightedScoreEnum,
|
WeightedScoreEnum,
|
||||||
} from '@/models/datasets'
|
} from '@/models/datasets'
|
||||||
|
import type { UploadFileSetting } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
export enum Theme {
|
export enum Theme {
|
||||||
light = 'light',
|
light = 'light',
|
||||||
@ -242,7 +243,7 @@ export type ModelConfig = {
|
|||||||
dataset_configs: DatasetConfigs
|
dataset_configs: DatasetConfigs
|
||||||
file_upload?: {
|
file_upload?: {
|
||||||
image: VisionSettings
|
image: VisionSettings
|
||||||
}
|
} & UploadFileSetting
|
||||||
files?: VisionFile[]
|
files?: VisionFile[]
|
||||||
created_at?: number
|
created_at?: number
|
||||||
updated_at?: number
|
updated_at?: number
|
||||||
|
Loading…
x
Reference in New Issue
Block a user