mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
Feat/plugins (#12547)
Co-authored-by: AkaraChen <akarachen@outlook.com> Co-authored-by: Yi <yxiaoisme@gmail.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: JzoNg <jzongcode@gmail.com> Co-authored-by: twwu <twwu@dify.ai> Co-authored-by: kurokobo <kuro664@gmail.com> Co-authored-by: Hiroshi Fujita <fujita-h@users.noreply.github.com>
This commit is contained in:
@ -12,7 +12,7 @@ import Button from '@/app/components/base/button'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { createEmptyDataset } from '@/service/datasets'
|
||||
|
||||
type IProps = {
|
||||
interface IProps {
|
||||
show: boolean
|
||||
onHide: () => void
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ import classNames from '@/utils/classnames'
|
||||
type IStepOneProps = {
|
||||
datasetId?: string
|
||||
dataSourceType?: DataSourceType
|
||||
dataSourceTypeDisable: Boolean
|
||||
dataSourceTypeDisable: boolean
|
||||
hasConnection: boolean
|
||||
onSetting: () => void
|
||||
files: FileItem[]
|
||||
|
||||
@ -6,7 +6,7 @@ import cn from '@/utils/classnames'
|
||||
import Popover from '@/app/components/base/popover'
|
||||
import { languages } from '@/i18n/language'
|
||||
|
||||
export type ILanguageSelectProps = {
|
||||
export interface ILanguageSelectProps {
|
||||
currentLanguage: string
|
||||
onSelect: (language: string) => void
|
||||
disabled?: boolean
|
||||
|
||||
@ -5,7 +5,7 @@ import cn from '@/utils/classnames'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
isChecked: boolean
|
||||
onChange: (isChecked: boolean) => void
|
||||
|
||||
@ -4,7 +4,7 @@ import React from 'react'
|
||||
import cn from '@/utils/classnames'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
title: string
|
||||
errorMsg?: string
|
||||
|
||||
@ -5,7 +5,7 @@ import Input from './input'
|
||||
import cn from '@/utils/classnames'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
label: string
|
||||
labelClassName?: string
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
value: string | number
|
||||
onChange: (value: string | number) => void
|
||||
placeholder?: string
|
||||
@ -20,7 +20,7 @@ const Input: FC<Props> = ({
|
||||
const handleChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = e.target.value
|
||||
if (isNumber) {
|
||||
let numberValue = parseInt(value, 10) // integer only
|
||||
let numberValue = Number.parseInt(value, 10) // integer only
|
||||
if (isNaN(numberValue)) {
|
||||
onChange('')
|
||||
return
|
||||
|
||||
@ -8,7 +8,7 @@ import { Settings04 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { ChevronRight } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
children: React.ReactNode
|
||||
controlFoldOptions?: number
|
||||
|
||||
@ -7,7 +7,7 @@ import Button from '@/app/components/base/button'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
isRunning: boolean
|
||||
onRun: (url: string) => void
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ import cn from '@/utils/classnames'
|
||||
import type { CrawlResultItem as CrawlResultItemType } from '@/models/datasets'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
payload: CrawlResultItemType
|
||||
isChecked: boolean
|
||||
isPreview: boolean
|
||||
|
||||
@ -9,7 +9,7 @@ import type { CrawlResultItem } from '@/models/datasets'
|
||||
|
||||
const I18N_PREFIX = 'datasetCreation.stepOne.website'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
list: CrawlResultItem[]
|
||||
checkedList: CrawlResultItem[]
|
||||
|
||||
@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RowStruct } from '@/app/components/base/icons/src/public/other'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
className?: string
|
||||
crawledNum: number
|
||||
totalNum: number
|
||||
|
||||
@ -79,7 +79,7 @@ export const useSegmentListContext = (selector: (value: SegmentListContextValue)
|
||||
return useContextSelector(SegmentListContext, selector)
|
||||
}
|
||||
|
||||
type ICompletedProps = {
|
||||
interface ICompletedProps {
|
||||
embeddingAvailable: boolean
|
||||
showNewSegmentModal: boolean
|
||||
onNewSegmentModalChange: (state: boolean) => void
|
||||
|
||||
@ -29,7 +29,7 @@ const map2Options = (map: { [key: string]: string }) => {
|
||||
return Object.keys(map).map(key => ({ value: key, name: map[key] }))
|
||||
}
|
||||
|
||||
type IFieldInfoProps = {
|
||||
interface IFieldInfoProps {
|
||||
label: string
|
||||
value?: string
|
||||
valueIcon?: ReactNode
|
||||
@ -117,7 +117,7 @@ const IconButton: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
type IMetadataProps = {
|
||||
interface IMetadataProps {
|
||||
docDetail?: FullDocumentDetail
|
||||
loading: boolean
|
||||
onUpdate: () => void
|
||||
|
||||
@ -0,0 +1,156 @@
|
||||
import { memo, useState } from 'react'
|
||||
import type { FC } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { RiCloseLine } from '@remixicon/react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common'
|
||||
import { Hash02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { SegmentUpdater } from '@/models/datasets'
|
||||
import { addSegment } from '@/service/datasets'
|
||||
import TagInput from '@/app/components/base/tag-input'
|
||||
|
||||
type NewSegmentModalProps = {
|
||||
isShow: boolean
|
||||
onCancel: () => void
|
||||
docForm: string
|
||||
onSave: () => void
|
||||
}
|
||||
|
||||
const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
||||
isShow,
|
||||
onCancel,
|
||||
docForm,
|
||||
onSave,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const { notify } = useContext(ToastContext)
|
||||
const [question, setQuestion] = useState('')
|
||||
const [answer, setAnswer] = useState('')
|
||||
const { datasetId, documentId } = useParams()
|
||||
const [keywords, setKeywords] = useState<string[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const handleCancel = () => {
|
||||
setQuestion('')
|
||||
setAnswer('')
|
||||
onCancel()
|
||||
setKeywords([])
|
||||
}
|
||||
|
||||
const handleSave = async () => {
|
||||
const params: SegmentUpdater = { content: '' }
|
||||
if (docForm === 'qa_model') {
|
||||
if (!question.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.questionEmpty') })
|
||||
if (!answer.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.answerEmpty') })
|
||||
|
||||
params.content = question
|
||||
params.answer = answer
|
||||
}
|
||||
else {
|
||||
if (!question.trim())
|
||||
return notify({ type: 'error', message: t('datasetDocuments.segment.contentEmpty') })
|
||||
|
||||
params.content = question
|
||||
}
|
||||
|
||||
if (keywords?.length)
|
||||
params.keywords = keywords
|
||||
|
||||
setLoading(true)
|
||||
try {
|
||||
await addSegment({ datasetId, documentId, body: params })
|
||||
notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') })
|
||||
handleCancel()
|
||||
onSave()
|
||||
}
|
||||
finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
const renderContent = () => {
|
||||
if (docForm === 'qa_model') {
|
||||
return (
|
||||
<>
|
||||
<div className='mb-1 text-xs font-medium text-gray-500'>QUESTION</div>
|
||||
<AutoHeightTextarea
|
||||
outerClassName='mb-4'
|
||||
className='leading-6 text-md text-gray-800'
|
||||
value={question}
|
||||
placeholder={t('datasetDocuments.segment.questionPlaceholder') || ''}
|
||||
onChange={e => setQuestion(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<div className='mb-1 text-xs font-medium text-gray-500'>ANSWER</div>
|
||||
<AutoHeightTextarea
|
||||
outerClassName='mb-4'
|
||||
className='leading-6 text-md text-gray-800'
|
||||
value={answer}
|
||||
placeholder={t('datasetDocuments.segment.answerPlaceholder') || ''}
|
||||
onChange={e => setAnswer(e.target.value)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<AutoHeightTextarea
|
||||
className='leading-6 text-md text-gray-800'
|
||||
value={question}
|
||||
placeholder={t('datasetDocuments.segment.contentPlaceholder') || ''}
|
||||
onChange={e => setQuestion(e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal isShow={isShow} onClose={() => { }} className='pt-8 px-8 pb-6 !max-w-[640px] !rounded-xl'>
|
||||
<div className={'flex flex-col relative'}>
|
||||
<div className='absolute right-0 -top-0.5 flex items-center h-6'>
|
||||
<div className='flex justify-center items-center w-6 h-6 cursor-pointer' onClick={handleCancel}>
|
||||
<RiCloseLine className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='mb-[14px]'>
|
||||
<span className='inline-flex items-center px-1.5 h-5 border border-gray-200 rounded-md'>
|
||||
<Hash02 className='mr-0.5 w-3 h-3 text-gray-400' />
|
||||
<span className='text-[11px] font-medium text-gray-500 italic'>
|
||||
{
|
||||
docForm === 'qa_model'
|
||||
? t('datasetDocuments.segment.newQaSegment')
|
||||
: t('datasetDocuments.segment.newTextSegment')
|
||||
}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
<div className='mb-4 py-1.5 h-[420px] overflow-auto'>{renderContent()}</div>
|
||||
<div className='text-xs font-medium text-gray-500'>{t('datasetDocuments.segment.keywords')}</div>
|
||||
<div className='mb-8'>
|
||||
<TagInput items={keywords} onChange={newKeywords => setKeywords(newKeywords)} />
|
||||
</div>
|
||||
<div className='flex justify-end space-x-2'>
|
||||
<Button
|
||||
onClick={handleCancel}>
|
||||
{t('common.operation.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleSave}
|
||||
disabled={loading}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(NewSegmentModal)
|
||||
@ -16,7 +16,7 @@ import { ModelTypeEnum } from '@/app/components/header/account-setting/model-pro
|
||||
import type { NotionPage } from '@/models/common'
|
||||
import { useDocumentDetail, useInvalidDocumentDetailKey } from '@/service/knowledge/use-document'
|
||||
|
||||
type DocumentSettingsProps = {
|
||||
interface DocumentSettingsProps {
|
||||
datasetId: string
|
||||
documentId: string
|
||||
}
|
||||
|
||||
@ -73,7 +73,7 @@ const EmptyElement: FC<{ canAdd: boolean; onClick: () => void; type?: 'upload' |
|
||||
</div>
|
||||
}
|
||||
|
||||
type IDocumentsProps = {
|
||||
interface IDocumentsProps {
|
||||
datasetId: string
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { renameDocumentName } from '@/service/datasets'
|
||||
|
||||
type Props = {
|
||||
interface Props {
|
||||
datasetId: string
|
||||
documentId: string
|
||||
name: string
|
||||
|
||||
@ -87,4 +87,6 @@ const Form: FC<FormProps> = React.memo(({
|
||||
)
|
||||
})
|
||||
|
||||
Form.displayName = 'Form'
|
||||
|
||||
export default Form
|
||||
|
||||
@ -15,7 +15,7 @@ import { asyncRunSafe } from '@/utils'
|
||||
import { RETRIEVE_METHOD, type RetrievalConfig } from '@/types/app'
|
||||
import promptS from '@/app/components/app/configuration/config-prompt/style.module.css'
|
||||
|
||||
type TextAreaWithButtonIProps = {
|
||||
interface TextAreaWithButtonIProps {
|
||||
datasetId: string
|
||||
onUpdateList: () => void
|
||||
setHitResult: (res: HitTestingResponse) => void
|
||||
|
||||
@ -14,7 +14,7 @@ import { ToastContext } from '@/app/components/base/toast'
|
||||
import type { DataSet } from '@/models/datasets'
|
||||
import { updateDatasetSetting } from '@/service/datasets'
|
||||
|
||||
type RenameDatasetModalProps = {
|
||||
interface RenameDatasetModalProps {
|
||||
show: boolean
|
||||
dataset: DataSet
|
||||
onSuccess?: () => void
|
||||
|
||||
@ -19,7 +19,7 @@ import { ApiConnectionMod } from '@/app/components/base/icons/src/vender/solid/d
|
||||
import { updateDatasetSetting } from '@/service/datasets'
|
||||
import { type DataSetListResponse } from '@/models/datasets'
|
||||
import DatasetDetailContext from '@/context/dataset-detail'
|
||||
import { type RetrievalConfig } from '@/types/app'
|
||||
import type { RetrievalConfig } from '@/types/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { isReRankModelSelected } from '@/app/components/datasets/common/check-rerank-model'
|
||||
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
|
||||
|
||||
@ -15,7 +15,7 @@ import { Users01, UsersPlus } from '@/app/components/base/icons/src/vender/solid
|
||||
import type { DatasetPermission } from '@/models/datasets'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import type { Member } from '@/models/common'
|
||||
export type RoleSelectorProps = {
|
||||
export interface RoleSelectorProps {
|
||||
disabled?: boolean
|
||||
permission?: DatasetPermission
|
||||
value: string[]
|
||||
|
||||
Reference in New Issue
Block a user