mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 01:48:04 +08:00
feat: frontend part of support try apps (#31287)
Co-authored-by: CodingOnStar <hanxujiang@dify.ai> Co-authored-by: yyh <92089059+lyzno1@users.noreply.github.com>
This commit is contained in:
@ -34,7 +34,7 @@ import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
import useDocumentTitle from '@/hooks/use-document-title'
|
||||
import { changeLanguage } from '@/i18n-config/client'
|
||||
import { AccessMode } from '@/models/access-control'
|
||||
import { fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share'
|
||||
import { AppSourceType, fetchSavedMessage as doFetchSavedMessage, removeMessage, saveMessage } from '@/service/share'
|
||||
import { Resolution, TransferMethod } from '@/types/app'
|
||||
import { cn } from '@/utils/classnames'
|
||||
import { userInputsFormToPromptVariables } from '@/utils/model-config'
|
||||
@ -69,10 +69,10 @@ export type IMainProps = {
|
||||
|
||||
const TextGeneration: FC<IMainProps> = ({
|
||||
isInstalledApp = false,
|
||||
installedAppInfo,
|
||||
isWorkflow = false,
|
||||
}) => {
|
||||
const { notify } = Toast
|
||||
const appSourceType = isInstalledApp ? AppSourceType.installedApp : AppSourceType.webApp
|
||||
|
||||
const { t } = useTranslation()
|
||||
const media = useBreakpoints()
|
||||
@ -102,16 +102,18 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
// save message
|
||||
const [savedMessages, setSavedMessages] = useState<SavedMessage[]>([])
|
||||
const fetchSavedMessage = useCallback(async () => {
|
||||
const res: any = await doFetchSavedMessage(isInstalledApp, appId)
|
||||
if (!appId)
|
||||
return
|
||||
const res: any = await doFetchSavedMessage(appSourceType, appId)
|
||||
setSavedMessages(res.data)
|
||||
}, [isInstalledApp, appId])
|
||||
}, [appSourceType, appId])
|
||||
const handleSaveMessage = async (messageId: string) => {
|
||||
await saveMessage(messageId, isInstalledApp, appId)
|
||||
await saveMessage(messageId, appSourceType, appId)
|
||||
notify({ type: 'success', message: t('api.saved', { ns: 'common' }) })
|
||||
fetchSavedMessage()
|
||||
}
|
||||
const handleRemoveSavedMessage = async (messageId: string) => {
|
||||
await removeMessage(messageId, isInstalledApp, appId)
|
||||
await removeMessage(messageId, appSourceType, appId)
|
||||
notify({ type: 'success', message: t('api.remove', { ns: 'common' }) })
|
||||
fetchSavedMessage()
|
||||
}
|
||||
@ -423,9 +425,8 @@ const TextGeneration: FC<IMainProps> = ({
|
||||
isCallBatchAPI={isCallBatchAPI}
|
||||
isPC={isPC}
|
||||
isMobile={!isPC}
|
||||
isInstalledApp={isInstalledApp}
|
||||
appSourceType={isInstalledApp ? AppSourceType.installedApp : AppSourceType.webApp}
|
||||
appId={appId}
|
||||
installedAppInfo={installedAppInfo}
|
||||
isError={task?.status === TaskStatus.failed}
|
||||
promptConfig={promptConfig}
|
||||
moreLikeThisEnabled={!!moreLikeThisConfig?.enabled}
|
||||
|
||||
@ -4,8 +4,8 @@ import type { FeedbackType } from '@/app/components/base/chat/chat/type'
|
||||
import type { WorkflowProcess } from '@/app/components/base/chat/types'
|
||||
import type { FileEntity } from '@/app/components/base/file-uploader/types'
|
||||
import type { PromptConfig } from '@/models/debug'
|
||||
import type { InstalledApp } from '@/models/explore'
|
||||
import type { SiteInfo } from '@/models/share'
|
||||
import type { AppSourceType } from '@/service/share'
|
||||
import type { VisionFile, VisionSettings } from '@/types/app'
|
||||
import { RiLoader2Line } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
@ -35,9 +35,8 @@ export type IResultProps = {
|
||||
isCallBatchAPI: boolean
|
||||
isPC: boolean
|
||||
isMobile: boolean
|
||||
isInstalledApp: boolean
|
||||
appId: string
|
||||
installedAppInfo?: InstalledApp
|
||||
appSourceType: AppSourceType
|
||||
appId?: string
|
||||
isError: boolean
|
||||
isShowTextToSpeech: boolean
|
||||
promptConfig: PromptConfig | null
|
||||
@ -63,9 +62,8 @@ const Result: FC<IResultProps> = ({
|
||||
isCallBatchAPI,
|
||||
isPC,
|
||||
isMobile,
|
||||
isInstalledApp,
|
||||
appSourceType,
|
||||
appId,
|
||||
installedAppInfo,
|
||||
isError,
|
||||
isShowTextToSpeech,
|
||||
promptConfig,
|
||||
@ -133,7 +131,7 @@ const Result: FC<IResultProps> = ({
|
||||
})
|
||||
|
||||
const handleFeedback = async (feedback: FeedbackType) => {
|
||||
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, isInstalledApp, installedAppInfo?.id)
|
||||
await updateFeedback({ url: `/messages/${messageId}/feedbacks`, body: { rating: feedback.rating, content: feedback.content } }, appSourceType, appId)
|
||||
setFeedback(feedback)
|
||||
}
|
||||
|
||||
@ -147,9 +145,9 @@ const Result: FC<IResultProps> = ({
|
||||
setIsStopping(true)
|
||||
try {
|
||||
if (isWorkflow)
|
||||
await stopWorkflowMessage(appId, currentTaskId, isInstalledApp, installedAppInfo?.id || '')
|
||||
await stopWorkflowMessage(appId!, currentTaskId, appSourceType, appId || '')
|
||||
else
|
||||
await stopChatMessageResponding(appId, currentTaskId, isInstalledApp, installedAppInfo?.id || '')
|
||||
await stopChatMessageResponding(appId!, currentTaskId, appSourceType, appId || '')
|
||||
abortControllerRef.current?.abort()
|
||||
}
|
||||
catch (error) {
|
||||
@ -159,7 +157,7 @@ const Result: FC<IResultProps> = ({
|
||||
finally {
|
||||
setIsStopping(false)
|
||||
}
|
||||
}, [appId, currentTaskId, installedAppInfo?.id, isInstalledApp, isStopping, isWorkflow, notify])
|
||||
}, [appId, currentTaskId, appSourceType, appId, isStopping, isWorkflow, notify])
|
||||
|
||||
useEffect(() => {
|
||||
if (!onRunControlChange)
|
||||
@ -468,8 +466,8 @@ const Result: FC<IResultProps> = ({
|
||||
}))
|
||||
},
|
||||
},
|
||||
isInstalledApp,
|
||||
installedAppInfo?.id,
|
||||
appSourceType,
|
||||
appId,
|
||||
).catch((error) => {
|
||||
setRespondingFalse()
|
||||
resetRunState()
|
||||
@ -514,7 +512,7 @@ const Result: FC<IResultProps> = ({
|
||||
getAbortController: (abortController) => {
|
||||
abortControllerRef.current = abortController
|
||||
},
|
||||
}, isInstalledApp, installedAppInfo?.id)
|
||||
}, appSourceType, appId)
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,8 +560,8 @@ const Result: FC<IResultProps> = ({
|
||||
feedback={feedback}
|
||||
onSave={handleSaveMessage}
|
||||
isMobile={isMobile}
|
||||
isInstalledApp={isInstalledApp}
|
||||
installedAppId={installedAppInfo?.id}
|
||||
appSourceType={appSourceType}
|
||||
installedAppId={appId}
|
||||
isLoading={isCallBatchAPI ? (!completionRes && isResponding) : false}
|
||||
taskId={isCallBatchAPI ? ((taskId as number) < 10 ? `0${taskId}` : `${taskId}`) : undefined}
|
||||
controlClearMoreLikeThis={controlClearMoreLikeThis}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import type { ChangeEvent, FC, FormEvent } from 'react'
|
||||
import type { InputValueTypes } from '../types'
|
||||
import type { PromptConfig } from '@/models/debug'
|
||||
import type { SiteInfo } from '@/models/share'
|
||||
import type { VisionFile, VisionSettings } from '@/types/app'
|
||||
@ -25,9 +26,9 @@ import { cn } from '@/utils/classnames'
|
||||
export type IRunOnceProps = {
|
||||
siteInfo: SiteInfo
|
||||
promptConfig: PromptConfig
|
||||
inputs: Record<string, any>
|
||||
inputsRef: React.RefObject<Record<string, any>>
|
||||
onInputsChange: (inputs: Record<string, any>) => void
|
||||
inputs: Record<string, InputValueTypes>
|
||||
inputsRef: React.RefObject<Record<string, InputValueTypes>>
|
||||
onInputsChange: (inputs: Record<string, InputValueTypes>) => void
|
||||
onSend: () => void
|
||||
visionConfig: VisionSettings
|
||||
onVisionFilesChange: (files: VisionFile[]) => void
|
||||
@ -52,7 +53,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
const [isInitialized, setIsInitialized] = useState(false)
|
||||
|
||||
const onClear = () => {
|
||||
const newInputs: Record<string, any> = {}
|
||||
const newInputs: Record<string, InputValueTypes> = {}
|
||||
promptConfig.prompt_variables.forEach((item) => {
|
||||
if (item.type === 'string' || item.type === 'paragraph')
|
||||
newInputs[item.key] = ''
|
||||
@ -127,7 +128,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
{item.type === 'select' && (
|
||||
<Select
|
||||
className="w-full"
|
||||
defaultValue={inputs[item.key]}
|
||||
defaultValue={inputs[item.key] as (string | number | undefined)}
|
||||
onSelect={(i) => { handleInputsChange({ ...inputsRef.current, [item.key]: i.value }) }}
|
||||
items={(item.options || []).map(i => ({ name: i, value: i }))}
|
||||
allowSearch={false}
|
||||
@ -137,7 +138,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
<Input
|
||||
type="text"
|
||||
placeholder={item.name}
|
||||
value={inputs[item.key]}
|
||||
value={inputs[item.key] as string}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => { handleInputsChange({ ...inputsRef.current, [item.key]: e.target.value }) }}
|
||||
maxLength={item.max_length}
|
||||
/>
|
||||
@ -146,7 +147,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
<Textarea
|
||||
className="h-[104px] sm:text-xs"
|
||||
placeholder={item.name}
|
||||
value={inputs[item.key]}
|
||||
value={inputs[item.key] as string}
|
||||
onChange={(e: ChangeEvent<HTMLTextAreaElement>) => { handleInputsChange({ ...inputsRef.current, [item.key]: e.target.value }) }}
|
||||
/>
|
||||
)}
|
||||
@ -154,14 +155,14 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
<Input
|
||||
type="number"
|
||||
placeholder={item.name}
|
||||
value={inputs[item.key]}
|
||||
value={inputs[item.key] as number}
|
||||
onChange={(e: ChangeEvent<HTMLInputElement>) => { handleInputsChange({ ...inputsRef.current, [item.key]: e.target.value }) }}
|
||||
/>
|
||||
)}
|
||||
{item.type === 'checkbox' && (
|
||||
<BoolInput
|
||||
name={item.name || item.key}
|
||||
value={!!inputs[item.key]}
|
||||
value={!!inputs[item.key] as boolean}
|
||||
required={item.required}
|
||||
onChange={(value) => { handleInputsChange({ ...inputsRef.current, [item.key]: value }) }}
|
||||
/>
|
||||
@ -182,6 +183,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
onChange={(files) => { handleInputsChange({ ...inputsRef.current, [item.key]: files }) }}
|
||||
fileConfig={{
|
||||
...item.config,
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
fileUploadConfig: (visionConfig as any).fileUploadConfig,
|
||||
}}
|
||||
/>
|
||||
@ -189,7 +191,7 @@ const RunOnce: FC<IRunOnceProps> = ({
|
||||
{item.type === 'json_object' && (
|
||||
<CodeEditor
|
||||
language={CodeLanguage.json}
|
||||
value={inputs[item.key]}
|
||||
value={inputs[item.key] as string}
|
||||
onChange={(value) => { handleInputsChange({ ...inputsRef.current, [item.key]: value }) }}
|
||||
noWrapper
|
||||
className="bg h-[80px] overflow-y-auto rounded-[10px] bg-components-input-bg-normal p-1"
|
||||
|
||||
19
web/app/components/share/text-generation/types.ts
Normal file
19
web/app/components/share/text-generation/types.ts
Normal file
@ -0,0 +1,19 @@
|
||||
type TaskParam = {
|
||||
inputs: Record<string, string | boolean | undefined>
|
||||
}
|
||||
|
||||
export type Task = {
|
||||
id: number
|
||||
status: TaskStatus
|
||||
params: TaskParam
|
||||
}
|
||||
|
||||
export enum TaskStatus {
|
||||
pending = 'pending',
|
||||
running = 'running',
|
||||
completed = 'completed',
|
||||
failed = 'failed',
|
||||
}
|
||||
|
||||
// eslint-disable-next-line ts/no-explicit-any
|
||||
export type InputValueTypes = string | boolean | number | string[] | object | undefined | any
|
||||
Reference in New Issue
Block a user