You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

app-inputs-panel.tsx 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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.checkbox) {
  76. return {
  77. ...item.checkbox,
  78. type: 'checkbox',
  79. required: false,
  80. }
  81. }
  82. if (item.select) {
  83. return {
  84. ...item.select,
  85. type: 'select',
  86. required: false,
  87. }
  88. }
  89. if (item['file-list']) {
  90. return {
  91. ...item['file-list'],
  92. type: 'file-list',
  93. required: false,
  94. fileUploadConfig,
  95. }
  96. }
  97. if (item.file) {
  98. return {
  99. ...item.file,
  100. type: 'file',
  101. required: false,
  102. fileUploadConfig,
  103. }
  104. }
  105. if (item.json_object) {
  106. return {
  107. ...item.json_object,
  108. type: 'json_object',
  109. }
  110. }
  111. return {
  112. ...item['text-input'],
  113. type: 'text-input',
  114. required: false,
  115. }
  116. }) || []
  117. }
  118. else {
  119. const startNode = currentWorkflow?.graph?.nodes.find(node => node.data.type === BlockEnum.Start) as any
  120. inputFormSchema = startNode?.data.variables.map((variable: any) => {
  121. if (variable.type === InputVarType.multiFiles) {
  122. return {
  123. ...variable,
  124. required: false,
  125. fileUploadConfig,
  126. }
  127. }
  128. if (variable.type === InputVarType.singleFile) {
  129. return {
  130. ...variable,
  131. required: false,
  132. fileUploadConfig,
  133. }
  134. }
  135. return {
  136. ...variable,
  137. required: false,
  138. }
  139. }) || []
  140. }
  141. if ((currentApp.mode === 'completion' || currentApp.mode === 'workflow') && basicAppFileConfig.enabled) {
  142. inputFormSchema.push({
  143. label: 'Image Upload',
  144. variable: '#image#',
  145. type: InputVarType.singleFile,
  146. required: false,
  147. ...basicAppFileConfig,
  148. fileUploadConfig,
  149. })
  150. }
  151. return inputFormSchema || []
  152. }, [basicAppFileConfig, currentApp, currentWorkflow, fileUploadConfig, isBasicApp])
  153. const handleFormChange = (value: Record<string, any>) => {
  154. inputsRef.current = value
  155. onFormChange(value)
  156. }
  157. return (
  158. <div className={cn('flex max-h-[240px] flex-col rounded-b-2xl border-t border-divider-subtle pb-4')}>
  159. {isLoading && <div className='pt-3'><Loading type='app' /></div>}
  160. {!isLoading && (
  161. <div className='system-sm-semibold mb-2 mt-3 flex h-6 shrink-0 items-center px-4 text-text-secondary'>{t('app.appSelector.params')}</div>
  162. )}
  163. {!isLoading && !inputFormSchema.length && (
  164. <div className='flex h-16 flex-col items-center justify-center'>
  165. <div className='system-sm-regular text-text-tertiary'>{t('app.appSelector.noParams')}</div>
  166. </div>
  167. )}
  168. {!isLoading && !!inputFormSchema.length && (
  169. <div className='grow overflow-y-auto'>
  170. <AppInputsForm
  171. inputs={value?.inputs || {}}
  172. inputsRef={inputsRef}
  173. inputsForms={inputFormSchema}
  174. onFormChange={handleFormChange}
  175. />
  176. </div>
  177. )}
  178. </div>
  179. )
  180. }
  181. export default AppInputsPanel