Spaces:
Paused
Paused
| 'use client' | |
| import type { FC } from 'react' | |
| import React, { useEffect, useState } from 'react' | |
| import { useTranslation } from 'react-i18next' | |
| import cn from 'classnames' | |
| import Button from '../base/button' | |
| import { Plus } from '../base/icons/src/vender/line/general' | |
| import Toast from '../base/toast' | |
| import type { Collection, CustomCollectionBackend, Tool } from './types' | |
| import { CollectionType, LOC } from './types' | |
| import ToolNavList from './tool-nav-list' | |
| import Search from './search' | |
| import Contribute from './contribute' | |
| import ToolList from './tool-list' | |
| import EditCustomToolModal from './edit-custom-collection-modal' | |
| import NoCustomTool from './info/no-custom-tool' | |
| import NoSearchRes from './info/no-search-res' | |
| import NoCustomToolPlaceholder from './no-custom-tool-placeholder' | |
| import { useTabSearchParams } from '@/hooks/use-tab-searchparams' | |
| import TabSlider from '@/app/components/base/tab-slider' | |
| import { createCustomCollection, fetchCollectionList as doFetchCollectionList, fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList } from '@/service/tools' | |
| import type { AgentTool } from '@/types/app' | |
| type Props = { | |
| loc: LOC | |
| addedTools?: AgentTool[] | |
| onAddTool?: (collection: Collection, payload: Tool) => void | |
| selectedProviderId?: string | |
| } | |
| const Tools: FC<Props> = ({ | |
| loc, | |
| addedTools, | |
| onAddTool, | |
| selectedProviderId, | |
| }) => { | |
| const { t } = useTranslation() | |
| const isInToolsPage = loc === LOC.tools | |
| const isInDebugPage = !isInToolsPage | |
| const [collectionList, setCollectionList] = useState<Collection[]>([]) | |
| const [currCollectionIndex, setCurrCollectionIndex] = useState<number | null>(null) | |
| const [isDetailLoading, setIsDetailLoading] = useState(false) | |
| const fetchCollectionList = async () => { | |
| const list = await doFetchCollectionList() | |
| setCollectionList(list) | |
| if (list.length > 0 && currCollectionIndex === null) { | |
| let index = 0 | |
| if (selectedProviderId) | |
| index = list.findIndex(item => item.id === selectedProviderId) | |
| setCurrCollectionIndex(index || 0) | |
| } | |
| } | |
| useEffect(() => { | |
| fetchCollectionList() | |
| }, []) | |
| const collectionTypeOptions = (() => { | |
| const res = [ | |
| { value: CollectionType.builtIn, text: t('tools.type.builtIn') }, | |
| { value: CollectionType.custom, text: t('tools.type.custom') }, | |
| ] | |
| if (!isInToolsPage) | |
| res.unshift({ value: CollectionType.all, text: t('tools.type.all') }) | |
| return res | |
| })() | |
| const [query, setQuery] = useState('') | |
| const [toolPageCollectionType, setToolPageCollectionType] = useTabSearchParams({ | |
| defaultTab: collectionTypeOptions[0].value, | |
| }) | |
| const [appPageCollectionType, setAppPageCollectionType] = useState(collectionTypeOptions[0].value) | |
| const { collectionType, setCollectionType } = (() => { | |
| if (isInToolsPage) { | |
| return { | |
| collectionType: toolPageCollectionType, | |
| setCollectionType: setToolPageCollectionType, | |
| } | |
| } | |
| return { | |
| collectionType: appPageCollectionType, | |
| setCollectionType: setAppPageCollectionType, | |
| } | |
| })() | |
| const showCollectionList = (() => { | |
| let typeFilteredList: Collection[] = [] | |
| if (collectionType === CollectionType.all) | |
| typeFilteredList = collectionList.filter(item => item.type !== CollectionType.model) | |
| else if (collectionType === CollectionType.builtIn) | |
| typeFilteredList = collectionList.filter(item => item.type === CollectionType.builtIn) | |
| else if (collectionType === CollectionType.custom) | |
| typeFilteredList = collectionList.filter(item => item.type === CollectionType.custom) | |
| if (query) | |
| return typeFilteredList.filter(item => item.name.includes(query)) | |
| return typeFilteredList | |
| })() | |
| const hasNoCustomCollection = !collectionList.find(item => item.type === CollectionType.custom) | |
| useEffect(() => { | |
| setCurrCollectionIndex(0) | |
| }, [collectionType]) | |
| const currCollection = (() => { | |
| if (currCollectionIndex === null) | |
| return null | |
| return showCollectionList[currCollectionIndex] | |
| })() | |
| const [currTools, setCurrentTools] = useState<Tool[]>([]) | |
| useEffect(() => { | |
| if (!currCollection) | |
| return | |
| (async () => { | |
| setIsDetailLoading(true) | |
| try { | |
| if (currCollection.type === CollectionType.builtIn) { | |
| const list = await fetchBuiltInToolList(currCollection.name) | |
| setCurrentTools(list) | |
| } | |
| else if (currCollection.type === CollectionType.model) { | |
| const list = await fetchModelToolList(currCollection.name) | |
| setCurrentTools(list) | |
| } | |
| else { | |
| const list = await fetchCustomToolList(currCollection.name) | |
| setCurrentTools(list) | |
| } | |
| } | |
| catch (e) { } | |
| setIsDetailLoading(false) | |
| })() | |
| }, [currCollection?.name, currCollection?.type]) | |
| const [isShowEditCollectionToolModal, setIsShowEditCollectionToolModal] = useState(false) | |
| const handleCreateToolCollection = () => { | |
| setIsShowEditCollectionToolModal(true) | |
| } | |
| const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => { | |
| await createCustomCollection(data) | |
| Toast.notify({ | |
| type: 'success', | |
| message: t('common.api.actionSuccess'), | |
| }) | |
| await fetchCollectionList() | |
| setIsShowEditCollectionToolModal(false) | |
| } | |
| return ( | |
| <> | |
| <div className='flex h-full'> | |
| {/* sidebar */} | |
| <div className={cn(isInToolsPage ? 'sm:w-[216px] px-4' : 'sm:w-[256px] px-3', 'flex flex-col w-16 shrink-0 pb-2')}> | |
| {isInToolsPage && ( | |
| <Button className='mt-6 flex items-center !h-8 pl-4' type='primary' onClick={handleCreateToolCollection}> | |
| <Plus className='w-4 h-4 mr-1' /> | |
| <div className='leading-[18px] text-[13px] font-medium truncate'>{t('tools.createCustomTool')}</div> | |
| </Button> | |
| )} | |
| {isInDebugPage && ( | |
| <div className='mt-6 flex space-x-1 items-center'> | |
| <Search | |
| className='grow' | |
| value={query} | |
| onChange={setQuery} | |
| /> | |
| <Button className='flex items-center justify-center !w-8 !h-8 !p-0' type='primary'> | |
| <Plus className='w-4 h-4' onClick={handleCreateToolCollection} /> | |
| </Button> | |
| </div> | |
| )} | |
| <TabSlider | |
| className='mt-3' | |
| itemWidth={isInToolsPage ? 89 : 75} | |
| value={collectionType} | |
| onChange={v => setCollectionType(v as CollectionType)} | |
| options={collectionTypeOptions} | |
| /> | |
| {isInToolsPage && ( | |
| <Search | |
| className='mt-5' | |
| value={query} | |
| onChange={setQuery} | |
| /> | |
| )} | |
| {(collectionType === CollectionType.custom && hasNoCustomCollection) | |
| ? ( | |
| <div className='grow h-0 p-2 pt-8'> | |
| <NoCustomTool onCreateTool={handleCreateToolCollection} /> | |
| </div> | |
| ) | |
| : ( | |
| (showCollectionList.length > 0 || !query) | |
| ? <ToolNavList | |
| className='mt-2 grow height-0 overflow-y-auto' | |
| currentIndex={currCollectionIndex || 0} | |
| list={showCollectionList} | |
| onChosen={setCurrCollectionIndex} | |
| /> | |
| : ( | |
| <div className='grow h-0 p-2 pt-8'> | |
| <NoSearchRes | |
| onReset={() => { setQuery('') }} | |
| /> | |
| </div> | |
| ) | |
| )} | |
| {loc === LOC.tools && ( | |
| <Contribute /> | |
| )} | |
| </div> | |
| {/* tools */} | |
| <div className={cn('grow h-full overflow-hidden p-2')}> | |
| <div className='h-full bg-white rounded-2xl'> | |
| {!(collectionType === CollectionType.custom && hasNoCustomCollection) && showCollectionList.length > 0 && ( | |
| <ToolList | |
| collection={currCollection} | |
| list={currTools} | |
| loc={loc} | |
| addedTools={addedTools} | |
| onAddTool={onAddTool} | |
| onRefreshData={fetchCollectionList} | |
| onCollectionRemoved={() => { | |
| setCurrCollectionIndex(0) | |
| fetchCollectionList() | |
| }} | |
| isLoading={isDetailLoading} | |
| /> | |
| )} | |
| {collectionType === CollectionType.custom && hasNoCustomCollection && ( | |
| <NoCustomToolPlaceholder /> | |
| )} | |
| </div> | |
| </div> | |
| </div> | |
| {isShowEditCollectionToolModal && ( | |
| <EditCustomToolModal | |
| payload={null} | |
| onHide={() => setIsShowEditCollectionToolModal(false)} | |
| onAdd={doCreateCustomToolCollection} | |
| /> | |
| )} | |
| </> | |
| ) | |
| } | |
| export default React.memo(Tools) | |