add tool selector in endpoint modal

This commit is contained in:
JzoNg 2024-11-14 00:01:47 +08:00
parent 194a99220b
commit ff1d42bd66
4 changed files with 158 additions and 29 deletions

View File

@ -18,6 +18,7 @@ import { SimpleSelect } from '@/app/components/base/select'
import Tooltip from '@/app/components/base/tooltip' import Tooltip from '@/app/components/base/tooltip'
import Radio from '@/app/components/base/radio' import Radio from '@/app/components/base/radio'
import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal' import ModelParameterModal from '@/app/components/header/account-setting/model-provider-page/model-parameter-modal'
import ToolSelector from '@/app/components/tools/tool-selector'
type FormProps = { type FormProps = {
className?: string className?: string
@ -317,7 +318,32 @@ const Form: FC<FormProps> = ({
} }
if (formSchema.type === FormTypeEnum.toolSelector) { if (formSchema.type === FormTypeEnum.toolSelector) {
// TODO const {
variable,
label,
required,
} = formSchema as (CredentialFormSchemaTextInput | CredentialFormSchemaSecretInput)
return (
<div key={variable} className={cn(itemClassName, 'py-3')}>
<div className={cn(fieldLabelClassName, 'flex items-center py-2 system-sm-semibold text-text-secondary')}>
{label[language] || label.en_US}
{
required && (
<span className='ml-1 text-red-500'>*</span>
)
}
{tooltipContent}
</div>
<ToolSelector
disabled={readonly}
value={value[variable]}
onSelect={item => handleFormChange(variable, item as any)}
/>
{fieldMoreInfo?.(formSchema)}
{validating && changeKey === variable && <ValidatingTip />}
</div>
)
} }
if (formSchema.type === FormTypeEnum.appSelector) { if (formSchema.type === FormTypeEnum.appSelector) {

View File

@ -1,6 +1,6 @@
'use client' 'use client'
import type { FC } from 'react' import type { FC } from 'react'
import React, { useState } from 'react' import React from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react' import { RiArrowRightUpLine, RiCloseLine } from '@remixicon/react'
import ActionButton from '@/app/components/base/action-button' import ActionButton from '@/app/components/base/action-button'
@ -11,8 +11,6 @@ import Toast from '@/app/components/base/toast'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import cn from '@/utils/classnames' import cn from '@/utils/classnames'
import ToolSelector from '@/app/components/tools/tool-selector'
type Props = { type Props = {
formSchemas: any formSchemas: any
defaultValues?: any defaultValues?: any
@ -40,11 +38,6 @@ const EndpointModal: FC<Props> = ({
onSaved(tempCredential) onSaved(tempCredential)
} }
const [mockTool, setTool] = useState<any>({
provider: 'langgenius/google/google',
tool_name: 'google_search',
})
return ( return (
<Drawer <Drawer
isOpen isOpen
@ -88,7 +81,6 @@ const EndpointModal: FC<Props> = ({
</a>) </a>)
: null} : null}
/> />
<ToolSelector disabled={false} value={mockTool} onSelect={setTool} />
</div> </div>
<div className={cn('p-4 pt-0 flex justify-end')} > <div className={cn('p-4 pt-0 flex justify-end')} >
<div className='flex gap-2'> <div className='flex gap-2'>

View File

@ -9,12 +9,18 @@ import {
} from '@/app/components/base/portal-to-follow-elem' } from '@/app/components/base/portal-to-follow-elem'
import ToolTrigger from '@/app/components/tools/tool-selector/tool-trigger' import ToolTrigger from '@/app/components/tools/tool-selector/tool-trigger'
import ToolPicker from '@/app/components/workflow/block-selector/tool-picker' import ToolPicker from '@/app/components/workflow/block-selector/tool-picker'
import Loading from '@/app/components/base/loading'
import Button from '@/app/components/base/button' import Button from '@/app/components/base/button'
import Indicator from '@/app/components/header/indicator' import Indicator from '@/app/components/header/indicator'
import ToolCredentialForm from '@/app/components/tools/tool-selector/tool-credentials-form'
import Toast from '@/app/components/base/toast'
import { useAppContext } from '@/context/app-context' import { useAppContext } from '@/context/app-context'
import { useAllBuiltInTools, useAllCustomTools, useAllWorkflowTools } from '@/service/use-tools' import {
useAllBuiltInTools,
useAllCustomTools,
useAllWorkflowTools,
useUpdateProviderCredentials,
} from '@/service/use-tools'
import { CollectionType } from '@/app/components/tools/types' import { CollectionType } from '@/app/components/tools/types'
import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types' import type { ToolDefaultValue } from '@/app/components/workflow/block-selector/types'
import type { import type {
@ -28,7 +34,7 @@ type Props = {
provider: string provider: string
tool_name: string tool_name: string
} }
disabled: boolean disabled?: boolean
placement?: Placement placement?: Placement
offset?: OffsetOptions offset?: OffsetOptions
onSelect: (tool: { onSelect: (tool: {
@ -72,18 +78,19 @@ const ToolSelector: FC<Props> = ({
onShowChange(false) onShowChange(false)
} }
const { isCurrentWorkspaceManager } = useAppContext() const { isCurrentWorkspaceManager } = useAppContext()
const [authLoading, setAuthLoading] = useState(false) const [isShowSettingAuth, setShowSettingAuth] = useState(false)
const handleCredentialSettingUpdate = () => {
Toast.notify({
type: 'success',
message: t('common.api.actionSuccess'),
})
setShowSettingAuth(false)
onShowChange(false)
}
// const [isShowSettingAuth, setShowSettingAuth] = useState(false) const { mutate: updatePermission } = useUpdateProviderCredentials({
onSuccess: handleCredentialSettingUpdate,
// const handleToolAuthSetting = (value: any) => { })
// const newModelConfig = produce(modelConfig, (draft) => {
// const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === value?.collection?.id && item.tool_name === value?.tool_name)
// if (tool)
// (tool as AgentTool).notAuthor = false
// })
// setModelConfig(newModelConfig)
// }
return ( return (
<> <>
@ -125,10 +132,19 @@ const ToolSelector: FC<Props> = ({
/> />
</div> </div>
{/* authorization panel */} {/* authorization panel */}
{authLoading && ( {isShowSettingAuth && currentProvider && (
<div className='px-4 py-3 flex items-center justify-center'><Loading type='app' /></div> <div className='px-4 pb-4 border-t border-divider-subtle'>
<ToolCredentialForm
collection={currentProvider}
onCancel={() => setShowSettingAuth(false)}
onSaved={async value => updatePermission({
providerName: currentProvider.name,
credentials: value,
})}
/>
</div>
)} )}
{!authLoading && currentProvider && currentProvider.type === CollectionType.builtIn && currentProvider.is_team_authorization && currentProvider.allow_delete && ( {!isShowSettingAuth && currentProvider && currentProvider.type === CollectionType.builtIn && currentProvider.is_team_authorization && currentProvider.allow_delete && (
<div className='px-4 py-3 flex items-center border-t border-divider-subtle'> <div className='px-4 py-3 flex items-center border-t border-divider-subtle'>
<div className='grow mr-3 h-6 flex items-center system-sm-semibold text-text-secondary'>{t('tools.toolSelector.auth')}</div> <div className='grow mr-3 h-6 flex items-center system-sm-semibold text-text-secondary'>{t('tools.toolSelector.auth')}</div>
{isCurrentWorkspaceManager && ( {isCurrentWorkspaceManager && (
@ -143,12 +159,12 @@ const ToolSelector: FC<Props> = ({
)} )}
</div> </div>
)} )}
{!authLoading && currentProvider && currentProvider.type === CollectionType.builtIn && !currentProvider.is_team_authorization && currentProvider.allow_delete && ( {!isShowSettingAuth && currentProvider && currentProvider.type === CollectionType.builtIn && !currentProvider.is_team_authorization && currentProvider.allow_delete && (
<div className='px-4 py-3 flex items-center border-t border-divider-subtle'> <div className='px-4 py-3 flex items-center border-t border-divider-subtle'>
<Button <Button
variant='primary' variant='primary'
className={cn('shrink-0 w-full')} className={cn('shrink-0 w-full')}
onClick={() => {}} onClick={() => setShowSettingAuth(true)}
disabled={!isCurrentWorkspaceManager} disabled={!isCurrentWorkspaceManager}
> >
{t('tools.auth.unauthorized')} {t('tools.auth.unauthorized')}

View File

@ -0,0 +1,95 @@
'use client'
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import {
RiArrowRightUpLine,
} from '@remixicon/react'
import { addDefaultValue, toolCredentialToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
import type { Collection } from '@/app/components/tools/types'
import Button from '@/app/components/base/button'
import Toast from '@/app/components/base/toast'
import { fetchBuiltInToolCredential, fetchBuiltInToolCredentialSchema } from '@/service/tools'
import Loading from '@/app/components/base/loading'
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks'
import cn from '@/utils/classnames'
type Props = {
collection: Collection
onCancel: () => void
onSaved: (value: Record<string, any>) => void
}
const ToolCredentialForm: FC<Props> = ({
collection,
onCancel,
onSaved,
}) => {
const { t } = useTranslation()
const language = useLanguage()
const [credentialSchema, setCredentialSchema] = useState<any>(null)
const { name: collectionName } = collection
const [tempCredential, setTempCredential] = React.useState<any>({})
useEffect(() => {
fetchBuiltInToolCredentialSchema(collectionName).then(async (res) => {
const toolCredentialSchemas = toolCredentialToFormSchemas(res)
const credentialValue = await fetchBuiltInToolCredential(collectionName)
setTempCredential(credentialValue)
const defaultCredentials = addDefaultValue(credentialValue, toolCredentialSchemas)
setCredentialSchema(toolCredentialSchemas)
setTempCredential(defaultCredentials)
})
}, [])
const handleSave = () => {
for (const field of credentialSchema) {
if (field.required && !tempCredential[field.name]) {
Toast.notify({ type: 'error', message: t('common.errorMsg.fieldRequired', { field: field.label[language] || field.label.en_US }) })
return
}
}
onSaved(tempCredential)
}
return (
<div className='h-full'>
{!credentialSchema
? <div className='pt-3'><Loading type='app' /></div>
: (
<>
<Form
value={tempCredential}
onChange={(v) => {
setTempCredential(v)
}}
formSchemas={credentialSchema}
isEditMode={true}
showOnVariableMap={{}}
validating={false}
inputClassName='bg-components-input-bg-normal hover:bg-state-base-hover-alt'
fieldMoreInfo={item => item.url
? (<a
href={item.url}
target='_blank' rel='noopener noreferrer'
className='inline-flex items-center text-xs text-primary-600'
>
{t('tools.howToGet')}
<RiArrowRightUpLine className='ml-1 w-3 h-3' />
</a>)
: null}
/>
<div className={cn('mt-1 flex justify-end')} >
<div className='flex space-x-2'>
<Button onClick={onCancel}>{t('common.operation.cancel')}</Button>
<Button variant='primary' onClick={handleSave}>{t('common.operation.save')}</Button>
</div>
</div>
</>
)
}
</div >
)
}
export default React.memo(ToolCredentialForm)