mirror of https://github.com/langgenius/dify.git
feat: enhance agent tools with end user credential support
This commit is contained in:
parent
1400b9c6e2
commit
cb4670cd68
|
|
@ -33,7 +33,7 @@ import { useAllBuiltInTools, useAllCustomTools, useAllMCPTools, useAllWorkflowTo
|
|||
import type { ToolWithProvider } from '@/app/components/workflow/types'
|
||||
import { useMittContextSelector } from '@/context/mitt-context'
|
||||
|
||||
type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null
|
||||
type AgentToolWithMoreInfo = (AgentTool & { icon: any; collection?: Collection; use_end_user_credentials?: boolean; end_user_credential_type?: string }) | null
|
||||
const AgentTools: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const [isShowChooseTool, setIsShowChooseTool] = useState(false)
|
||||
|
|
@ -102,7 +102,9 @@ const AgentTools: FC = () => {
|
|||
tool_parameters: tool.params,
|
||||
notAuthor: !tool.is_team_authorization,
|
||||
enabled: true,
|
||||
}
|
||||
use_end_user_credentials: false,
|
||||
end_user_credential_type: '',
|
||||
} as any
|
||||
}
|
||||
const handleSelectTool = (tool: ToolDefaultValue) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
|
|
@ -138,6 +140,34 @@ const AgentTools: FC = () => {
|
|||
formattingChangedDispatcher()
|
||||
}, [currentTool, modelConfig, setModelConfig, formattingChangedDispatcher])
|
||||
|
||||
const handleEndUserCredentialChange = useCallback((enabled: boolean) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === currentTool?.provider_id)
|
||||
if (tool)
|
||||
(tool as AgentTool).use_end_user_credentials = enabled
|
||||
})
|
||||
setCurrentTool({
|
||||
...currentTool,
|
||||
use_end_user_credentials: enabled,
|
||||
} as any)
|
||||
setModelConfig(newModelConfig)
|
||||
formattingChangedDispatcher()
|
||||
}, [currentTool, modelConfig, setModelConfig, formattingChangedDispatcher])
|
||||
|
||||
const handleEndUserCredentialTypeChange = useCallback((type: string) => {
|
||||
const newModelConfig = produce(modelConfig, (draft) => {
|
||||
const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === currentTool?.provider_id)
|
||||
if (tool)
|
||||
(tool as AgentTool).end_user_credential_type = type
|
||||
})
|
||||
setCurrentTool({
|
||||
...currentTool,
|
||||
end_user_credential_type: type,
|
||||
} as any)
|
||||
setModelConfig(newModelConfig)
|
||||
formattingChangedDispatcher()
|
||||
}, [currentTool, modelConfig, setModelConfig, formattingChangedDispatcher])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Panel
|
||||
|
|
@ -315,6 +345,10 @@ const AgentTools: FC = () => {
|
|||
onHide={() => setIsShowSettingTool(false)}
|
||||
credentialId={currentTool?.credential_id}
|
||||
onAuthorizationItemClick={handleAuthorizationItemClick}
|
||||
useEndUserCredentialEnabled={currentTool?.use_end_user_credentials}
|
||||
endUserCredentialType={currentTool?.end_user_credential_type}
|
||||
onEndUserCredentialChange={handleEndUserCredentialChange}
|
||||
onEndUserCredentialTypeChange={handleEndUserCredentialTypeChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,10 @@ type Props = {
|
|||
onSave?: (value: Record<string, any>) => void
|
||||
credentialId?: string
|
||||
onAuthorizationItemClick?: (id: string) => void
|
||||
useEndUserCredentialEnabled?: boolean
|
||||
endUserCredentialType?: string
|
||||
onEndUserCredentialChange?: (enabled: boolean) => void
|
||||
onEndUserCredentialTypeChange?: (type: string) => void
|
||||
}
|
||||
|
||||
const SettingBuiltInTool: FC<Props> = ({
|
||||
|
|
@ -56,6 +60,10 @@ const SettingBuiltInTool: FC<Props> = ({
|
|||
onSave,
|
||||
credentialId,
|
||||
onAuthorizationItemClick,
|
||||
useEndUserCredentialEnabled,
|
||||
endUserCredentialType,
|
||||
onEndUserCredentialChange,
|
||||
onEndUserCredentialTypeChange,
|
||||
}) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const language = getLanguage(locale)
|
||||
|
|
@ -220,6 +228,10 @@ const SettingBuiltInTool: FC<Props> = ({
|
|||
}}
|
||||
credentialId={credentialId}
|
||||
onAuthorizationItemClick={onAuthorizationItemClick}
|
||||
useEndUserCredentialEnabled={useEndUserCredentialEnabled}
|
||||
endUserCredentialType={endUserCredentialType}
|
||||
onEndUserCredentialChange={onEndUserCredentialChange}
|
||||
onEndUserCredentialTypeChange={onEndUserCredentialTypeChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export const usePluginAuth = (pluginPayload: PluginPayload, enable?: boolean) =>
|
|||
const canOAuth = data?.supported_credential_types.includes(CredentialTypeEnum.OAUTH2)
|
||||
const canApiKey = data?.supported_credential_types.includes(CredentialTypeEnum.API_KEY)
|
||||
const invalidPluginCredentialInfo = useInvalidPluginCredentialInfoHook(pluginPayload)
|
||||
const hasOAuthClientConfigured = data?.is_oauth_custom_client_enabled
|
||||
|
||||
return {
|
||||
isAuthorized,
|
||||
|
|
@ -22,5 +23,6 @@ export const usePluginAuth = (pluginPayload: PluginPayload, enable?: boolean) =>
|
|||
disabled: !isCurrentWorkspaceManager,
|
||||
notAllowCustomCredential: data?.allow_custom_token === false,
|
||||
invalidPluginCredentialInfo,
|
||||
hasOAuthClientConfigured: !!hasOAuthClientConfigured,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,23 @@
|
|||
import {
|
||||
memo,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react'
|
||||
import { RiArrowDownSLine } from '@remixicon/react'
|
||||
import type { ReactNode } from 'react'
|
||||
import {
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiEqualizer2Line,
|
||||
RiKey2Line,
|
||||
RiUserStarLine,
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Authorize from './authorize'
|
||||
import Authorized from './authorized'
|
||||
import AddOAuthButton from './authorize/add-oauth-button'
|
||||
import AddApiKeyButton from './authorize/add-api-key-button'
|
||||
import type {
|
||||
Credential,
|
||||
PluginPayload,
|
||||
|
|
@ -15,19 +26,35 @@ import { usePluginAuth } from './hooks/use-plugin-auth'
|
|||
import Button from '@/app/components/base/button'
|
||||
import Indicator from '@/app/components/header/indicator'
|
||||
import cn from '@/utils/classnames'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
type PluginAuthInAgentProps = {
|
||||
pluginPayload: PluginPayload
|
||||
credentialId?: string
|
||||
onAuthorizationItemClick?: (id: string) => void
|
||||
useEndUserCredentialEnabled?: boolean
|
||||
endUserCredentialType?: string
|
||||
onEndUserCredentialChange?: (enabled: boolean) => void
|
||||
onEndUserCredentialTypeChange?: (type: string) => void
|
||||
}
|
||||
const PluginAuthInAgent = ({
|
||||
pluginPayload,
|
||||
credentialId,
|
||||
onAuthorizationItemClick,
|
||||
useEndUserCredentialEnabled,
|
||||
endUserCredentialType,
|
||||
onEndUserCredentialChange,
|
||||
onEndUserCredentialTypeChange,
|
||||
}: PluginAuthInAgentProps) => {
|
||||
const { t } = useTranslation()
|
||||
const [isOpen, setIsOpen] = useState(false)
|
||||
const [showAddMenu, setShowAddMenu] = useState(false)
|
||||
const [showEndUserTypeMenu, setShowEndUserTypeMenu] = useState(false)
|
||||
const {
|
||||
isAuthorized,
|
||||
canOAuth,
|
||||
|
|
@ -36,6 +63,7 @@ const PluginAuthInAgent = ({
|
|||
disabled,
|
||||
invalidPluginCredentialInfo,
|
||||
notAllowCustomCredential,
|
||||
hasOAuthClientConfigured,
|
||||
} = usePluginAuth(pluginPayload, true)
|
||||
|
||||
const extraAuthorizationItems: Credential[] = [
|
||||
|
|
@ -94,10 +122,219 @@ const PluginAuthInAgent = ({
|
|||
)
|
||||
}, [credentialId, credentials, t])
|
||||
|
||||
const availableEndUserTypes = useMemo(() => {
|
||||
const list: { value: string; label: string; icon: ReactNode }[] = []
|
||||
if (canOAuth) {
|
||||
list.push({
|
||||
value: 'oauth2',
|
||||
label: t('plugin.auth.endUserCredentials.optionOAuth'),
|
||||
icon: <RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />,
|
||||
})
|
||||
}
|
||||
if (canApiKey) {
|
||||
list.push({
|
||||
value: 'api-key',
|
||||
label: t('plugin.auth.endUserCredentials.optionApiKey'),
|
||||
icon: <RiKey2Line className='h-4 w-4 text-text-tertiary' />,
|
||||
})
|
||||
}
|
||||
return list
|
||||
}, [canOAuth, canApiKey, t])
|
||||
|
||||
const endUserCredentialLabel = useMemo(() => {
|
||||
const found = availableEndUserTypes.find(item => item.value === endUserCredentialType)
|
||||
return found?.label || availableEndUserTypes[0]?.label || '-'
|
||||
}, [availableEndUserTypes, endUserCredentialType])
|
||||
|
||||
useEffect(() => {
|
||||
if (!useEndUserCredentialEnabled)
|
||||
return
|
||||
if (!availableEndUserTypes.length)
|
||||
return
|
||||
const isValid = availableEndUserTypes.some(item => item.value === endUserCredentialType)
|
||||
if (!isValid)
|
||||
onEndUserCredentialTypeChange?.(availableEndUserTypes[0].value)
|
||||
}, [useEndUserCredentialEnabled, endUserCredentialType, availableEndUserTypes, onEndUserCredentialTypeChange])
|
||||
|
||||
const handleSelectEndUserType = useCallback((value: string) => {
|
||||
onEndUserCredentialTypeChange?.(value)
|
||||
setShowEndUserTypeMenu(false)
|
||||
}, [onEndUserCredentialTypeChange])
|
||||
|
||||
const shouldShowAuthorizeCard = !credentials.length && (canOAuth || canApiKey || hasOAuthClientConfigured)
|
||||
|
||||
const endUserSection = (
|
||||
<div className='flex items-start rounded-lg border-components-panel-border-subtle bg-components-panel-on-panel-item-bg px-3 py-3'>
|
||||
<RiUserStarLine className='mt-0.5 h-4 w-4 text-text-tertiary' />
|
||||
<div className='flex-1 space-y-3'>
|
||||
<div className='flex items-center justify-between gap-3'>
|
||||
<div className='space-y-1'>
|
||||
<div className='system-sm-semibold text-text-primary'>
|
||||
{t('plugin.auth.endUserCredentials.title')}
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>
|
||||
{t('plugin.auth.endUserCredentials.desc')}
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
size='md'
|
||||
defaultValue={!!useEndUserCredentialEnabled}
|
||||
onChange={onEndUserCredentialChange}
|
||||
disabled={disabled}
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
useEndUserCredentialEnabled && availableEndUserTypes.length > 0 && (
|
||||
<div className='flex items-center justify-between gap-3'>
|
||||
<div className='system-sm-semibold text-text-primary'>
|
||||
{t('plugin.auth.endUserCredentials.typeLabel')}
|
||||
</div>
|
||||
<PortalToFollowElem
|
||||
open={showEndUserTypeMenu}
|
||||
onOpenChange={setShowEndUserTypeMenu}
|
||||
placement='bottom-end'
|
||||
offset={6}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<button
|
||||
type='button'
|
||||
className='border-components-input-border flex h-9 min-w-[190px] items-center justify-between rounded-lg border bg-components-input-bg-normal px-3 text-left text-text-primary shadow-xs hover:bg-components-input-bg-hover'
|
||||
onClick={() => setShowEndUserTypeMenu(v => !v)}
|
||||
>
|
||||
<span className='system-sm-semibold'>{endUserCredentialLabel}</span>
|
||||
<RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />
|
||||
</button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[120]'>
|
||||
<div className='w-[220px] rounded-xl border border-components-panel-border bg-components-panel-bg shadow-lg'>
|
||||
<div className='flex flex-col gap-1 p-1'>
|
||||
{canOAuth && (
|
||||
<AddOAuthButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
className='w-full justify-between bg-transparent text-text-primary hover:bg-transparent'
|
||||
buttonText={t('plugin.auth.addOAuth')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
handleSelectEndUserType('oauth2')
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canApiKey && (
|
||||
<AddApiKeyButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
buttonText={t('plugin.auth.addApi')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
handleSelectEndUserType('api-key')
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='border-components-panel-border bg-components-panel-bg'>
|
||||
<div className='flex items-start justify-between gap-2'>
|
||||
<div className='flex items-start gap-2'>
|
||||
<RiKey2Line className='mt-0.5 h-4 w-4 text-text-tertiary' />
|
||||
<div className='space-y-0.5'>
|
||||
<div className='system-md-semibold text-text-primary'>
|
||||
{t('plugin.auth.configuredCredentials.title')}
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>
|
||||
{t('plugin.auth.configuredCredentials.desc')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PortalToFollowElem
|
||||
open={showAddMenu}
|
||||
onOpenChange={setShowAddMenu}
|
||||
placement='bottom-end'
|
||||
offset={6}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<button
|
||||
type='button'
|
||||
className={cn(
|
||||
'flex h-9 w-9 items-center justify-center rounded-full bg-primary-600 text-white hover:bg-primary-700',
|
||||
(disabled || (!canOAuth && !canApiKey && !hasOAuthClientConfigured)) && 'pointer-events-none opacity-50',
|
||||
)}
|
||||
onClick={() => setShowAddMenu(v => !v)}
|
||||
>
|
||||
<RiAddLine className='h-5 w-5' />
|
||||
</button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[120]'>
|
||||
<div className='w-[220px] rounded-xl border border-components-panel-border bg-components-panel-bg shadow-lg'>
|
||||
<div className='flex flex-col gap-1 p-1'>
|
||||
{
|
||||
canOAuth && (
|
||||
<AddOAuthButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
className='w-full justify-between bg-transparent text-text-primary hover:bg-transparent'
|
||||
buttonText={t('plugin.auth.addOAuth')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
setShowAddMenu(false)
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
canApiKey && (
|
||||
<AddApiKeyButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
buttonText={t('plugin.auth.addApi')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
setShowAddMenu(false)
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
{
|
||||
!isAuthorized && (
|
||||
!isAuthorized && shouldShowAuthorizeCard && (
|
||||
<div className='rounded-xl bg-background-section px-4 py-4'>
|
||||
<div className='flex w-full justify-center'>
|
||||
<div className='w-full max-w-[520px]'>
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled}
|
||||
onUpdate={invalidPluginCredentialInfo}
|
||||
notAllowCustomCredential={notAllowCustomCredential}
|
||||
theme='secondary'
|
||||
showDivider={!!(canOAuth && canApiKey)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
{
|
||||
!isAuthorized && !shouldShowAuthorizeCard && (
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
canOAuth={canOAuth}
|
||||
|
|
@ -129,7 +366,8 @@ const PluginAuthInAgent = ({
|
|||
/>
|
||||
)
|
||||
}
|
||||
</>
|
||||
{endUserSection}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import type { ReactNode } from 'react'
|
|||
import {
|
||||
RiAddLine,
|
||||
RiArrowDownSLine,
|
||||
RiCheckLine,
|
||||
RiEqualizer2Line,
|
||||
RiKey2Line,
|
||||
RiUserStarLine,
|
||||
|
|
@ -20,7 +19,6 @@ import {
|
|||
PortalToFollowElemContent,
|
||||
PortalToFollowElemTrigger,
|
||||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import Authorize from './authorize'
|
||||
import Authorized from './authorized'
|
||||
import AddApiKeyButton from './authorize/add-api-key-button'
|
||||
|
|
@ -60,11 +58,18 @@ const PluginAuth = ({
|
|||
disabled,
|
||||
invalidPluginCredentialInfo,
|
||||
notAllowCustomCredential,
|
||||
hasOAuthClientConfigured,
|
||||
} = usePluginAuth(pluginPayload, !!pluginPayload.provider)
|
||||
const shouldShowGuide = !!showConnectGuide
|
||||
const [showCredentialPanel, setShowCredentialPanel] = useState(false)
|
||||
const [showAddMenu, setShowAddMenu] = useState(false)
|
||||
const [showEndUserTypeMenu, setShowEndUserTypeMenu] = useState(false)
|
||||
const configuredDisabled = !!endUserCredentialEnabled
|
||||
const shouldShowAuthorizeCard = useMemo(() => {
|
||||
const hasCredential = credentials.length > 0
|
||||
const canAdd = canOAuth || canApiKey || hasOAuthClientConfigured
|
||||
return !hasCredential && canAdd
|
||||
}, [credentials.length, canOAuth, canApiKey, hasOAuthClientConfigured])
|
||||
const availableEndUserTypes = useMemo(() => {
|
||||
const list: { value: string; label: string; icon: ReactNode }[] = []
|
||||
if (canOAuth) {
|
||||
|
|
@ -100,6 +105,7 @@ const PluginAuth = ({
|
|||
|
||||
const handleSelectEndUserType = useCallback((value: string) => {
|
||||
onEndUserCredentialTypeChange?.(value)
|
||||
setShowEndUserTypeMenu(false)
|
||||
}, [onEndUserCredentialTypeChange])
|
||||
const containerClassName = useMemo(() => {
|
||||
if (showConnectGuide)
|
||||
|
|
@ -165,43 +171,54 @@ const PluginAuth = ({
|
|||
<div className='system-sm-semibold text-text-primary'>
|
||||
{t('plugin.auth.endUserCredentials.typeLabel')}
|
||||
</div>
|
||||
<SimpleSelect
|
||||
wrapperClassName='w-[190px]'
|
||||
items={availableEndUserTypes.map(item => ({
|
||||
value: item.value,
|
||||
name: item.label,
|
||||
icon: item.icon,
|
||||
}))}
|
||||
defaultValue={endUserCredentialType || availableEndUserTypes[0]?.value}
|
||||
disabled={disabled}
|
||||
onSelect={item => handleSelectEndUserType(item.value as string)}
|
||||
renderTrigger={(value, open) => (
|
||||
<PortalToFollowElem
|
||||
open={showEndUserTypeMenu}
|
||||
onOpenChange={setShowEndUserTypeMenu}
|
||||
placement='bottom-end'
|
||||
offset={6}
|
||||
>
|
||||
<PortalToFollowElemTrigger asChild>
|
||||
<button
|
||||
type='button'
|
||||
className='border-components-input-border flex h-9 w-full items-center justify-between rounded-lg border bg-components-input-bg-normal px-3 text-left text-text-primary shadow-xs hover:bg-components-input-bg-hover'
|
||||
className='border-components-input-border flex h-9 min-w-[190px] items-center justify-between rounded-lg border bg-components-input-bg-normal px-3 text-left text-text-primary shadow-xs hover:bg-components-input-bg-hover'
|
||||
onClick={() => setShowEndUserTypeMenu(v => !v)}
|
||||
>
|
||||
<span className='system-sm-semibold'>{value?.name || endUserCredentialLabel}</span>
|
||||
<RiArrowDownSLine
|
||||
className={cn(
|
||||
'h-4 w-4 text-text-tertiary transition-transform',
|
||||
open && 'rotate-180',
|
||||
)}
|
||||
/>
|
||||
<span className='system-sm-semibold'>{endUserCredentialLabel}</span>
|
||||
<RiArrowDownSLine className='h-4 w-4 text-text-tertiary' />
|
||||
</button>
|
||||
)}
|
||||
renderOption={({ item, selected }) => (
|
||||
<div className='flex items-center justify-between'>
|
||||
<div className='flex items-center gap-2'>
|
||||
{item.icon}
|
||||
<span className='system-sm-semibold text-text-primary'>{item.name}</span>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[120]'>
|
||||
<div className='w-[220px] rounded-xl border border-components-panel-border bg-components-panel-bg shadow-lg'>
|
||||
<div className='flex flex-col gap-1 p-1'>
|
||||
{canOAuth && (
|
||||
<AddOAuthButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
className='w-full justify-between bg-transparent text-text-primary hover:bg-transparent'
|
||||
buttonText={t('plugin.auth.addOAuth')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
handleSelectEndUserType('oauth2')
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{canApiKey && (
|
||||
<AddApiKeyButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
buttonText={t('plugin.auth.addApi')}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
handleSelectEndUserType('api-key')
|
||||
invalidPluginCredentialInfo()
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{selected && <RiCheckLine className='h-4 w-4 text-text-accent' />}
|
||||
</div>
|
||||
)}
|
||||
optionWrapClassName='p-1'
|
||||
optionClassName='px-3 py-2 rounded-lg'
|
||||
hideChecked
|
||||
/>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -282,8 +299,9 @@ const PluginAuth = ({
|
|||
<AddOAuthButton
|
||||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
className='w-full justify-between bg-transparent text-text-primary hover:bg-transparent'
|
||||
buttonText={t('plugin.auth.addOAuth')}
|
||||
disabled={disabled || configuredDisabled}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
setShowAddMenu(false)
|
||||
invalidPluginCredentialInfo()
|
||||
|
|
@ -297,7 +315,7 @@ const PluginAuth = ({
|
|||
pluginPayload={pluginPayload}
|
||||
buttonVariant='ghost'
|
||||
buttonText={t('plugin.auth.addApi')}
|
||||
disabled={disabled || configuredDisabled}
|
||||
disabled={disabled}
|
||||
onUpdate={() => {
|
||||
setShowAddMenu(false)
|
||||
invalidPluginCredentialInfo()
|
||||
|
|
@ -314,22 +332,24 @@ const PluginAuth = ({
|
|||
{credentialList}
|
||||
</div>
|
||||
{
|
||||
credentials.length === 0 && (
|
||||
shouldShowAuthorizeCard && (
|
||||
<div className={cn(
|
||||
'mt-4 flex items-start gap-1.5 rounded-xl bg-background-section px-4 py-8',
|
||||
configuredDisabled && 'pointer-events-none opacity-50',
|
||||
)}>
|
||||
<div className='flex w-full justify-center'>
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled || configuredDisabled}
|
||||
onUpdate={invalidPluginCredentialInfo}
|
||||
notAllowCustomCredential={notAllowCustomCredential}
|
||||
theme='secondary'
|
||||
showDivider={!!(canOAuth && canApiKey)}
|
||||
/>
|
||||
<div className='w-full max-w-[520px]'>
|
||||
<Authorize
|
||||
pluginPayload={pluginPayload}
|
||||
canOAuth={canOAuth}
|
||||
canApiKey={canApiKey}
|
||||
disabled={disabled || configuredDisabled}
|
||||
onUpdate={invalidPluginCredentialInfo}
|
||||
notAllowCustomCredential={notAllowCustomCredential}
|
||||
theme='secondary'
|
||||
showDivider={!!(canOAuth && canApiKey)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -210,6 +210,18 @@ const ToolSelector: FC<Props> = ({
|
|||
credential_id: id,
|
||||
} as any)
|
||||
}
|
||||
const handleEndUserCredentialChange = (enabled: boolean) => {
|
||||
onSelect({
|
||||
...value,
|
||||
use_end_user_credentials: enabled,
|
||||
} as any)
|
||||
}
|
||||
const handleEndUserCredentialTypeChange = (type: string) => {
|
||||
onSelect({
|
||||
...value,
|
||||
end_user_credential_type: type,
|
||||
} as any)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -323,6 +335,10 @@ const ToolSelector: FC<Props> = ({
|
|||
}}
|
||||
credentialId={value?.credential_id}
|
||||
onAuthorizationItemClick={handleAuthorizationItemClick}
|
||||
useEndUserCredentialEnabled={value?.use_end_user_credentials}
|
||||
endUserCredentialType={value?.end_user_credential_type}
|
||||
onEndUserCredentialChange={handleEndUserCredentialChange}
|
||||
onEndUserCredentialTypeChange={handleEndUserCredentialTypeChange}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ export type AgentTool = {
|
|||
isDeleted?: boolean
|
||||
notAuthor?: boolean
|
||||
credential_id?: string
|
||||
use_end_user_credentials?: boolean
|
||||
end_user_credential_type?: string
|
||||
}
|
||||
|
||||
export type ToolItem = {
|
||||
|
|
|
|||
Loading…
Reference in New Issue