app-inputs-panel.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. 'use client'
  2. import React, { useMemo, useRef } from 'react'
  3. import { useTranslation } from 'react-i18next'
  4. import Loading from '@/app/components/base/loading'
  5. import AppInputsForm from '@/app/components/plugins/plugin-detail-panel/app-selector/app-inputs-form'
  6. import { useAppDetail } from '@/service/use-apps'
  7. import { useAppWorkflow } from '@/service/use-workflow'
  8. import { useFileUploadConfig } from '@/service/use-common'
  9. import { Resolution } from '@/types/app'
  10. import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
  11. import type { App } from '@/types/app'
  12. import type { FileUpload } from '@/app/components/base/features/types'
  13. import { BlockEnum, InputVarType, SupportUploadFileTypes } from '@/app/components/workflow/types'
  14. import cn from '@/utils/classnames'
  15. type Props = {
  16. value?: {
  17. app_id: string
  18. inputs: Record<string, any>
  19. }
  20. appDetail: App
  21. onFormChange: (value: Record<string, any>) => void
  22. }
  23. const AppInputsPanel = ({
  24. value,
  25. appDetail,
  26. onFormChange,
  27. }: Props) => {
  28. const { t } = useTranslation()
  29. const inputsRef = useRef<any>(value?.inputs || {})
  30. const isBasicApp = appDetail.mode !== 'advanced-chat' && appDetail.mode !== 'workflow'
  31. const { data: fileUploadConfig } = useFileUploadConfig()
  32. const { data: currentApp, isFetching: isAppLoading } = useAppDetail(appDetail.id)
  33. const { data: currentWorkflow, isFetching: isWorkflowLoading } = useAppWorkflow(isBasicApp ? '' : appDetail.id)
  34. const isLoading = isAppLoading || isWorkflowLoading
  35. const basicAppFileConfig = useMemo(() => {
  36. let fileConfig: FileUpload
  37. if (isBasicApp)
  38. fileConfig = currentApp?.model_config?.file_upload as FileUpload
  39. else
  40. fileConfig = currentWorkflow?.features?.file_upload as FileUpload
  41. return {
  42. image: {
  43. detail: fileConfig?.image?.detail || Resolution.high,
  44. enabled: !!fileConfig?.image?.enabled,
  45. number_limits: fileConfig?.image?.number_limits || 3,
  46. transfer_methods: fileConfig?.image?.transfer_methods || ['local_file', 'remote_url'],
  47. },
  48. enabled: !!(fileConfig?.enabled || fileConfig?.image?.enabled),
  49. allowed_file_types: fileConfig?.allowed_file_types || [SupportUploadFileTypes.image],
  50. allowed_file_extensions: fileConfig?.allowed_file_extensions || [...FILE_EXTS[SupportUploadFileTypes.image]].map(ext => `.${ext}`),
  51. allowed_file_upload_methods: fileConfig?.allowed_file_upload_methods || fileConfig?.image?.transfer_methods || ['local_file', 'remote_url'],
  52. number_limits: fileConfig?.number_limits || fileConfig?.image?.number_limits || 3,
  53. }
  54. }, [currentApp?.model_config?.file_upload, currentWorkflow?.features?.file_upload, isBasicApp])
  55. const inputFormSchema = useMemo(() => {
  56. if (!currentApp)
  57. return []
  58. let inputFormSchema = []
  59. if (isBasicApp) {
  60. inputFormSchema = currentApp.model_config.user_input_form.filter((item: any) => !item.external_data_tool).map((item: any) => {
  61. if (item.paragraph) {
  62. return {
  63. ...item.paragraph,
  64. type: 'paragraph',
  65. required: false,
  66. }
  67. }
  68. if (item.number) {
  69. return {
  70. ...item.number,
  71. type: 'number',
  72. required: false,
  73. }
  74. }
  75. if (item.select) {
  76. return {
  77. ...item.select,
  78. type: 'select',
  79. required: false,
  80. }
  81. }
  82. if (item['file-list']) {
  83. return {
  84. ...item['file-list'],
  85. type: 'file-list',
  86. required: false,
  87. fileUploadConfig,
  88. }
  89. }
  90. if (item.file) {
  91. return {
  92. ...item.file,
  93. type: 'file',
  94. required: false,
  95. fileUploadConfig,
  96. }
  97. }
  98. return {
  99. ...item['text-input'],
  100. type: 'text-input',
  101. required: false,
  102. }
  103. })
  104. }
  105. else {
  106. const startNode = currentWorkflow?.graph.nodes.find(node => node.data.type === BlockEnum.Start) as any
  107. inputFormSchema = startNode?.data.variables.map((variable: any) => {
  108. if (variable.type === InputVarType.multiFiles) {
  109. return {
  110. ...variable,
  111. required: false,
  112. fileUploadConfig,
  113. }
  114. }
  115. if (variable.type === InputVarType.singleFile) {
  116. return {
  117. ...variable,
  118. required: false,
  119. fileUploadConfig,
  120. }
  121. }
  122. return {
  123. ...variable,
  124. required: false,
  125. }
  126. })
  127. }
  128. if ((currentApp.mode === 'completion' || currentApp.mode === 'workflow') && basicAppFileConfig.enabled) {
  129. inputFormSchema.push({
  130. label: 'Image Upload',
  131. variable: '#image#',
  132. type: InputVarType.singleFile,
  133. required: false,
  134. ...basicAppFileConfig,
  135. fileUploadConfig,
  136. })
  137. }
  138. return inputFormSchema
  139. }, [basicAppFileConfig, currentApp, currentWorkflow, fileUploadConfig, isBasicApp])
  140. const handleFormChange = (value: Record<string, any>) => {
  141. inputsRef.current = value
  142. onFormChange(value)
  143. }
  144. return (
  145. <div className={cn('max-h-[240px] flex flex-col pb-4 rounded-b-2xl border-t border-divider-subtle')}>
  146. {isLoading && <div className='pt-3'><Loading type='app' /></div>}
  147. {!isLoading && (
  148. <div className='shrink-0 mt-3 mb-2 px-4 h-6 flex items-center system-sm-semibold text-text-secondary'>{t('app.appSelector.params')}</div>
  149. )}
  150. {!isLoading && !inputFormSchema.length && (
  151. <div className='h-16 flex flex-col justify-center items-center'>
  152. <div className='text-text-tertiary system-sm-regular'>{t('app.appSelector.noParams')}</div>
  153. </div>
  154. )}
  155. {!isLoading && !!inputFormSchema.length && (
  156. <div className='grow overflow-y-auto'>
  157. <AppInputsForm
  158. inputs={value?.inputs || {}}
  159. inputsRef={inputsRef}
  160. inputsForms={inputFormSchema}
  161. onFormChange={handleFormChange}
  162. />
  163. </div>
  164. )}
  165. </div>
  166. )
  167. }
  168. export default AppInputsPanel