mirror of
https://github.com/langgenius/dify.git
synced 2026-05-04 09:28:04 +08:00
mrege main
This commit is contained in:
@ -29,7 +29,17 @@ export default function ChartView({ appId }: IChartViewProps) {
|
||||
const [period, setPeriod] = useState<PeriodParams>({ name: t('appLog.filter.period.last7days'), query: { start: today.subtract(7, 'day').format(queryDateFormat), end: today.format(queryDateFormat) } })
|
||||
|
||||
const onSelect = (item: Item) => {
|
||||
setPeriod({ name: item.name, query: item.value === 'all' ? undefined : { start: today.subtract(item.value as number, 'day').format(queryDateFormat), end: today.format(queryDateFormat) } })
|
||||
if (item.value === 'all') {
|
||||
setPeriod({ name: item.name, query: undefined })
|
||||
}
|
||||
else if (item.value === 0) {
|
||||
const startOfToday = today.startOf('day').format(queryDateFormat)
|
||||
const endOfToday = today.endOf('day').format(queryDateFormat)
|
||||
setPeriod({ name: item.name, query: { start: startOfToday, end: endOfToday } })
|
||||
}
|
||||
else {
|
||||
setPeriod({ name: item.name, query: { start: today.subtract(item.value as number, 'day').format(queryDateFormat), end: today.format(queryDateFormat) } })
|
||||
}
|
||||
}
|
||||
|
||||
if (!response)
|
||||
|
||||
@ -62,8 +62,10 @@ const ActivateForm = () => {
|
||||
showErrorMessage(t('login.error.passwordEmpty'))
|
||||
return false
|
||||
}
|
||||
if (!validPassword.test(password))
|
||||
if (!validPassword.test(password)) {
|
||||
showErrorMessage(t('login.error.passwordInvalid'))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}, [name, password, showErrorMessage, t])
|
||||
|
||||
@ -24,7 +24,7 @@ const WarningMask: FC<IWarningMaskProps> = ({
|
||||
return (
|
||||
<div className={`${s.mask} absolute z-10 inset-0 pt-16`}
|
||||
>
|
||||
<div className='mx-auto w-[535px]'>
|
||||
<div className='mx-auto px-10'>
|
||||
<div className={`${s.icon} flex items-center justify-center w-11 h-11 rounded-xl bg-white`}>{warningIcon}</div>
|
||||
<div className='mt-4 text-[24px] leading-normal font-semibold text-gray-800'>
|
||||
{title}
|
||||
|
||||
@ -25,6 +25,7 @@ import { useToastContext } from '@/app/components/base/toast'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { ADD_EXTERNAL_DATA_TOOL } from '@/app/components/app/configuration/config-var'
|
||||
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
|
||||
import { PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER } from '@/app/components/base/prompt-editor/plugins/update-block'
|
||||
|
||||
export type ISimplePromptInput = {
|
||||
mode: AppType
|
||||
@ -122,6 +123,10 @@ const Prompt: FC<ISimplePromptInput> = ({
|
||||
if (mode === AppType.chat)
|
||||
setIntroduction(res.opening_statement)
|
||||
showAutomaticFalse()
|
||||
eventEmitter?.emit({
|
||||
type: PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER,
|
||||
payload: res.prompt,
|
||||
} as any)
|
||||
}
|
||||
const minHeight = 228
|
||||
const [editorHeight, setEditorHeight] = useState(minHeight)
|
||||
|
||||
@ -34,7 +34,7 @@ const AgentTools: FC = () => {
|
||||
const [selectedProviderId, setSelectedProviderId] = useState<string | undefined>(undefined)
|
||||
const [isShowSettingTool, setIsShowSettingTool] = useState(false)
|
||||
const tools = (modelConfig?.agentConfig?.tools as AgentTool[] || []).map((item) => {
|
||||
const collection = collectionList.find(collection => collection.id === item.provider_id)
|
||||
const collection = collectionList.find(collection => collection.id === item.provider_id && collection.type === item.provider_type)
|
||||
const icon = collection?.icon
|
||||
return {
|
||||
...item,
|
||||
|
||||
@ -8,7 +8,7 @@ import Drawer from '@/app/components/base/drawer-plus'
|
||||
import Form from '@/app/components/header/account-setting/model-provider-page/model-modal/Form'
|
||||
import { addDefaultValue, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema'
|
||||
import type { Collection, Tool } from '@/app/components/tools/types'
|
||||
import { fetchBuiltInToolList, fetchCustomToolList } from '@/service/tools'
|
||||
import { fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList } from '@/service/tools'
|
||||
import I18n from '@/context/i18n'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Loading from '@/app/components/base/loading'
|
||||
@ -19,6 +19,7 @@ import AppIcon from '@/app/components/base/app-icon'
|
||||
type Props = {
|
||||
collection: Collection
|
||||
isBuiltIn?: boolean
|
||||
isModel?: boolean
|
||||
toolName: string
|
||||
setting?: Record<string, any>
|
||||
readonly?: boolean
|
||||
@ -29,6 +30,7 @@ type Props = {
|
||||
const SettingBuiltInTool: FC<Props> = ({
|
||||
collection,
|
||||
isBuiltIn = true,
|
||||
isModel = true,
|
||||
toolName,
|
||||
setting = {},
|
||||
readonly,
|
||||
@ -56,7 +58,11 @@ const SettingBuiltInTool: FC<Props> = ({
|
||||
(async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const list = isBuiltIn ? await fetchBuiltInToolList(collection.name) : await fetchCustomToolList(collection.name)
|
||||
const list = isBuiltIn
|
||||
? await fetchBuiltInToolList(collection.name)
|
||||
: isModel
|
||||
? await fetchModelToolList(collection.name)
|
||||
: await fetchCustomToolList(collection.name)
|
||||
setTools(list)
|
||||
const currTool = list.find(tool => tool.name === toolName)
|
||||
if (currTool) {
|
||||
|
||||
@ -130,7 +130,7 @@ const Debug: FC<IDebug> = ({
|
||||
|
||||
const { notify } = useContext(ToastContext)
|
||||
const logError = useCallback((message: string) => {
|
||||
notify({ type: 'error', message })
|
||||
notify({ type: 'error', message, duration: 3000 })
|
||||
}, [notify])
|
||||
const [completionFiles, setCompletionFiles] = useState<VisionFile[]>([])
|
||||
|
||||
|
||||
@ -12,6 +12,7 @@ import { SimpleSelect } from '@/app/components/base/select'
|
||||
import type { AppDetailResponse } from '@/models/app'
|
||||
import type { Language } from '@/types/app'
|
||||
import EmojiPicker from '@/app/components/base/emoji-picker'
|
||||
import { useToastContext } from '@/app/components/base/toast'
|
||||
|
||||
import { languages } from '@/i18n/language'
|
||||
|
||||
@ -42,6 +43,7 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
onClose,
|
||||
onSave,
|
||||
}) => {
|
||||
const { notify } = useToastContext()
|
||||
const [isShowMore, setIsShowMore] = useState(false)
|
||||
const { icon, icon_background } = appInfo
|
||||
const { title, description, copyright, privacy_policy, default_language } = appInfo.site
|
||||
@ -67,6 +69,10 @@ const SettingsModal: FC<ISettingsModalProps> = ({
|
||||
}
|
||||
|
||||
const onClickSave = async () => {
|
||||
if (!inputInfo.title) {
|
||||
notify({ type: 'error', message: t('app.newApp.nameNotEmpty') })
|
||||
return
|
||||
}
|
||||
setSaveLoading(true)
|
||||
const params = {
|
||||
title: inputInfo.title,
|
||||
|
||||
@ -95,7 +95,10 @@ const ConfigPanel = () => {
|
||||
<Button
|
||||
type='primary'
|
||||
className='mr-2 text-sm font-medium'
|
||||
onClick={handleStartChat}
|
||||
onClick={() => {
|
||||
setCollapsed(true)
|
||||
handleStartChat()
|
||||
}}
|
||||
>
|
||||
{t('common.operation.save')}
|
||||
</Button>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type { FC } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import cn from 'classnames'
|
||||
import Uploader from './uploader'
|
||||
import ImageLinkInput from './image-link-input'
|
||||
import { ImagePlus } from '@/app/components/base/icons/src/vender/line/images'
|
||||
@ -25,16 +26,16 @@ const UploadOnlyFromLocal: FC<UploadOnlyFromLocalProps> = ({
|
||||
}) => {
|
||||
return (
|
||||
<Uploader onUpload={onUpload} disabled={disabled} limit={limit}>
|
||||
{
|
||||
hovering => (
|
||||
<div className={`
|
||||
{hovering => (
|
||||
<div
|
||||
className={`
|
||||
relative flex items-center justify-center w-8 h-8 rounded-lg cursor-pointer
|
||||
${hovering && 'bg-gray-100'}
|
||||
`}>
|
||||
<ImagePlus className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
`}
|
||||
>
|
||||
<ImagePlus className="w-4 h-4 text-gray-500" />
|
||||
</div>
|
||||
)}
|
||||
</Uploader>
|
||||
)
|
||||
}
|
||||
@ -54,13 +55,16 @@ const UploaderButton: FC<UploaderButtonProps> = ({
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const hasUploadFromLocal = methods.find(method => method === TransferMethod.local_file)
|
||||
const hasUploadFromLocal = methods.find(
|
||||
method => method === TransferMethod.local_file,
|
||||
)
|
||||
|
||||
const handleUpload = (imageFile: ImageFile) => {
|
||||
setOpen(false)
|
||||
onUpload(imageFile)
|
||||
}
|
||||
|
||||
const closePopover = () => setOpen(false)
|
||||
|
||||
const handleToggle = () => {
|
||||
if (disabled)
|
||||
return
|
||||
@ -72,43 +76,46 @@ const UploaderButton: FC<UploaderButtonProps> = ({
|
||||
<PortalToFollowElem
|
||||
open={open}
|
||||
onOpenChange={setOpen}
|
||||
placement='top-start'
|
||||
placement="top-start"
|
||||
>
|
||||
<PortalToFollowElemTrigger onClick={handleToggle}>
|
||||
<div className={`
|
||||
relative flex items-center justify-center w-8 h-8 hover:bg-gray-100 rounded-lg
|
||||
${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}>
|
||||
<ImagePlus className='w-4 h-4 text-gray-500' />
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
className="relative flex items-center justify-center w-8 h-8 enabled:hover:bg-gray-100 rounded-lg disabled:cursor-not-allowed"
|
||||
>
|
||||
<ImagePlus className="w-4 h-4 text-gray-500" />
|
||||
</button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-50'>
|
||||
<div className='p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg'>
|
||||
<PortalToFollowElemContent className="z-50">
|
||||
<div className="p-2 w-[260px] bg-white rounded-lg border-[0.5px] border-gray-200 shadow-lg">
|
||||
<ImageLinkInput onUpload={handleUpload} />
|
||||
{
|
||||
hasUploadFromLocal && (
|
||||
<>
|
||||
<div className='flex items-center mt-2 px-2 text-xs font-medium text-gray-400'>
|
||||
<div className='mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]' />
|
||||
OR
|
||||
<div className='ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]' />
|
||||
</div>
|
||||
<Uploader onUpload={handleUpload} limit={limit}>
|
||||
{
|
||||
hovering => (
|
||||
<div className={`
|
||||
flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer
|
||||
${hovering && 'bg-primary-50'}
|
||||
`}>
|
||||
<Upload03 className='mr-1 w-4 h-4' />
|
||||
{t('common.imageUploader.uploadFromComputer')}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Uploader>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{hasUploadFromLocal && (
|
||||
<>
|
||||
<div className="flex items-center mt-2 px-2 text-xs font-medium text-gray-400">
|
||||
<div className="mr-3 w-[93px] h-[1px] bg-gradient-to-l from-[#F3F4F6]" />
|
||||
OR
|
||||
<div className="ml-3 w-[93px] h-[1px] bg-gradient-to-r from-[#F3F4F6]" />
|
||||
</div>
|
||||
<Uploader
|
||||
onUpload={handleUpload}
|
||||
limit={limit}
|
||||
closePopover={closePopover}
|
||||
>
|
||||
{hovering => (
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-center h-8 text-[13px] font-medium text-[#155EEF] rounded-lg cursor-pointer',
|
||||
hovering && 'bg-primary-50',
|
||||
)}
|
||||
>
|
||||
<Upload03 className="mr-1 w-4 h-4" />
|
||||
{t('common.imageUploader.uploadFromComputer')}
|
||||
</div>
|
||||
)}
|
||||
</Uploader>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</PortalToFollowElemContent>
|
||||
</PortalToFollowElem>
|
||||
@ -125,7 +132,9 @@ const ChatImageUploader: FC<ChatImageUploaderProps> = ({
|
||||
onUpload,
|
||||
disabled,
|
||||
}) => {
|
||||
const onlyUploadLocal = settings.transfer_methods.length === 1 && settings.transfer_methods[0] === TransferMethod.local_file
|
||||
const onlyUploadLocal
|
||||
= settings.transfer_methods.length === 1
|
||||
&& settings.transfer_methods[0] === TransferMethod.local_file
|
||||
|
||||
if (onlyUploadLocal) {
|
||||
return (
|
||||
|
||||
@ -30,6 +30,7 @@ const ImageLinkInput: FC<ImageLinkInputProps> = ({
|
||||
return (
|
||||
<div className='flex items-center pl-1.5 pr-1 h-8 border border-gray-200 bg-white shadow-xs rounded-lg'>
|
||||
<input
|
||||
type="text"
|
||||
className='grow mr-0.5 px-1 h-[18px] text-[13px] outline-none appearance-none'
|
||||
value={imageLink}
|
||||
onChange={e => setImageLink(e.target.value)}
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import type { FC } from 'react'
|
||||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Loading02, XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import cn from 'classnames'
|
||||
import {
|
||||
Loading02,
|
||||
XClose,
|
||||
} from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { RefreshCcw01 } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
@ -30,7 +34,11 @@ const ImageList: FC<ImageListProps> = ({
|
||||
const [imagePreviewUrl, setImagePreviewUrl] = useState('')
|
||||
|
||||
const handleImageLinkLoadSuccess = (item: ImageFile) => {
|
||||
if (item.type === TransferMethod.remote_url && onImageLinkLoadSuccess && item.progress !== -1)
|
||||
if (
|
||||
item.type === TransferMethod.remote_url
|
||||
&& onImageLinkLoadSuccess
|
||||
&& item.progress !== -1
|
||||
)
|
||||
onImageLinkLoadSuccess(item._id)
|
||||
}
|
||||
const handleImageLinkLoadError = (item: ImageFile) => {
|
||||
@ -39,89 +47,95 @@ const ImageList: FC<ImageListProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='flex flex-wrap'>
|
||||
{
|
||||
list.map(item => (
|
||||
<div
|
||||
key={item._id}
|
||||
className='group relative mr-1 border-[0.5px] border-black/5 rounded-lg'
|
||||
>
|
||||
{
|
||||
item.type === TransferMethod.local_file && item.progress !== 100 && (
|
||||
<>
|
||||
<div
|
||||
className='absolute inset-0 flex items-center justify-center z-[1] bg-black/30'
|
||||
style={{ left: item.progress > -1 ? `${item.progress}%` : 0 }}
|
||||
>
|
||||
{
|
||||
item.progress === -1 && (
|
||||
<RefreshCcw01 className='w-5 h-5 text-white' onClick={() => onReUpload && onReUpload(item._id)} />
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{
|
||||
item.progress > -1 && (
|
||||
<span className='absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] text-sm text-white mix-blend-lighten z-[1]'>{item.progress}%</span>
|
||||
)
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
item.type === TransferMethod.remote_url && item.progress !== 100 && (
|
||||
<div className={`
|
||||
<div className="flex flex-wrap">
|
||||
{list.map(item => (
|
||||
<div
|
||||
key={item._id}
|
||||
className="group relative mr-1 border-[0.5px] border-black/5 rounded-lg"
|
||||
>
|
||||
{item.type === TransferMethod.local_file && item.progress !== 100 && (
|
||||
<>
|
||||
<div
|
||||
className="absolute inset-0 flex items-center justify-center z-[1] bg-black/30"
|
||||
style={{ left: item.progress > -1 ? `${item.progress}%` : 0 }}
|
||||
>
|
||||
{item.progress === -1 && (
|
||||
<RefreshCcw01
|
||||
className="w-5 h-5 text-white"
|
||||
onClick={() => onReUpload && onReUpload(item._id)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{item.progress > -1 && (
|
||||
<span className="absolute top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%] text-sm text-white mix-blend-lighten z-[1]">
|
||||
{item.progress}%
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{item.type === TransferMethod.remote_url && item.progress !== 100 && (
|
||||
<div
|
||||
className={`
|
||||
absolute inset-0 flex items-center justify-center rounded-lg z-[1] border
|
||||
${item.progress === -1 ? 'bg-[#FEF0C7] border-[#DC6803]' : 'bg-black/[0.16] border-transparent'}
|
||||
`}>
|
||||
{
|
||||
item.progress > -1 && (
|
||||
<Loading02 className='animate-spin w-5 h-5 text-white' />
|
||||
)
|
||||
}
|
||||
{
|
||||
item.progress === -1 && (
|
||||
<TooltipPlus popupContent={t('common.imageUploader.pasteImageLinkInvalid')}>
|
||||
<AlertTriangle className='w-4 h-4 text-[#DC6803]' />
|
||||
</TooltipPlus>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)
|
||||
${
|
||||
item.progress === -1
|
||||
? 'bg-[#FEF0C7] border-[#DC6803]'
|
||||
: 'bg-black/[0.16] border-transparent'
|
||||
}
|
||||
<img
|
||||
className='w-16 h-16 rounded-lg object-cover cursor-pointer border-[0.5px] border-black/5'
|
||||
alt=''
|
||||
onLoad={() => handleImageLinkLoadSuccess(item)}
|
||||
onError={() => handleImageLinkLoadError(item)}
|
||||
src={item.type === TransferMethod.remote_url ? item.url : item.base64Url}
|
||||
onClick={() => item.progress === 100 && setImagePreviewUrl((item.type === TransferMethod.remote_url ? item.url : item.base64Url) as string)}
|
||||
/>
|
||||
{
|
||||
!readonly && (
|
||||
<div
|
||||
className={`
|
||||
absolute z-10 -top-[9px] -right-[9px] items-center justify-center w-[18px] h-[18px]
|
||||
bg-white hover:bg-gray-50 border-[0.5px] border-black/[0.02] rounded-2xl shadow-lg
|
||||
cursor-pointer
|
||||
${item.progress === -1 ? 'flex' : 'hidden group-hover:flex'}
|
||||
`}
|
||||
onClick={() => onRemove && onRemove(item._id)}
|
||||
`}
|
||||
>
|
||||
{item.progress > -1 && (
|
||||
<Loading02 className="animate-spin w-5 h-5 text-white" />
|
||||
)}
|
||||
{item.progress === -1 && (
|
||||
<TooltipPlus
|
||||
popupContent={t('common.imageUploader.pasteImageLinkInvalid')}
|
||||
>
|
||||
<XClose className='w-3 h-3 text-gray-500' />
|
||||
</div>
|
||||
<AlertTriangle className="w-4 h-4 text-[#DC6803]" />
|
||||
</TooltipPlus>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
className="w-16 h-16 rounded-lg object-cover cursor-pointer border-[0.5px] border-black/5"
|
||||
alt={item.file?.name}
|
||||
onLoad={() => handleImageLinkLoadSuccess(item)}
|
||||
onError={() => handleImageLinkLoadError(item)}
|
||||
src={
|
||||
item.type === TransferMethod.remote_url
|
||||
? item.url
|
||||
: item.base64Url
|
||||
}
|
||||
onClick={() =>
|
||||
item.progress === 100
|
||||
&& setImagePreviewUrl(
|
||||
(item.type === TransferMethod.remote_url
|
||||
? item.url
|
||||
: item.base64Url) as string,
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
{
|
||||
imagePreviewUrl && (
|
||||
<ImagePreview
|
||||
url={imagePreviewUrl}
|
||||
onCancel={() => setImagePreviewUrl('')}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{!readonly && (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'absolute z-10 -top-[9px] -right-[9px] items-center justify-center w-[18px] h-[18px]',
|
||||
'bg-white hover:bg-gray-50 border-[0.5px] border-black/[0.02] rounded-2xl shadow-lg',
|
||||
item.progress === -1 ? 'flex' : 'hidden group-hover:flex',
|
||||
)}
|
||||
onClick={() => onRemove && onRemove(item._id)}
|
||||
>
|
||||
<XClose className="w-3 h-3 text-gray-500" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{imagePreviewUrl && (
|
||||
<ImagePreview
|
||||
url={imagePreviewUrl}
|
||||
onCancel={() => setImagePreviewUrl('')}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import { ALLOW_FILE_EXTENSIONS } from '@/types/app'
|
||||
type UploaderProps = {
|
||||
children: (hovering: boolean) => JSX.Element
|
||||
onUpload: (imageFile: ImageFile) => void
|
||||
closePopover?: () => void
|
||||
limit?: number
|
||||
disabled?: boolean
|
||||
}
|
||||
@ -14,11 +15,16 @@ type UploaderProps = {
|
||||
const Uploader: FC<UploaderProps> = ({
|
||||
children,
|
||||
onUpload,
|
||||
closePopover,
|
||||
limit,
|
||||
disabled,
|
||||
}) => {
|
||||
const [hovering, setHovering] = useState(false)
|
||||
const { handleLocalFileUpload } = useLocalFileUploader({ limit, onUpload, disabled })
|
||||
const { handleLocalFileUpload } = useLocalFileUploader({
|
||||
limit,
|
||||
onUpload,
|
||||
disabled,
|
||||
})
|
||||
|
||||
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0]
|
||||
@ -27,6 +33,7 @@ const Uploader: FC<UploaderProps> = ({
|
||||
return
|
||||
|
||||
handleLocalFileUpload(file)
|
||||
closePopover?.()
|
||||
}
|
||||
|
||||
return (
|
||||
@ -37,11 +44,8 @@ const Uploader: FC<UploaderProps> = ({
|
||||
>
|
||||
{children(hovering)}
|
||||
<input
|
||||
className={`
|
||||
absolute block inset-0 opacity-0 text-[0] w-full
|
||||
${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}
|
||||
onClick={e => (e.target as HTMLInputElement).value = ''}
|
||||
className='absolute block inset-0 opacity-0 text-[0] w-full disabled:cursor-not-allowed cursor-pointer'
|
||||
onClick={e => ((e.target as HTMLInputElement).value = '')}
|
||||
type='file'
|
||||
accept={ALLOW_FILE_EXTENSIONS.map(ext => `.${ext}`).join(',')}
|
||||
onChange={handleChange}
|
||||
|
||||
@ -32,6 +32,7 @@ import VariableValueBlock from './plugins/variable-value-block'
|
||||
import { VariableValueBlockNode } from './plugins/variable-value-block/node'
|
||||
import { CustomTextNode } from './plugins/custom-text/node'
|
||||
import OnBlurBlock from './plugins/on-blur-or-focus-block'
|
||||
import UpdateBlock from './plugins/update-block'
|
||||
import { textToEditorState } from './utils'
|
||||
import type { Dataset } from './plugins/context-block'
|
||||
import type { RoleName } from './plugins/history-block'
|
||||
@ -226,6 +227,7 @@ const PromptEditor: FC<PromptEditorProps> = ({
|
||||
<VariableValueBlock />
|
||||
<OnChangePlugin onChange={handleEditorChange} />
|
||||
<OnBlurBlock onBlur={onBlur} onFocus={onFocus} />
|
||||
<UpdateBlock />
|
||||
{/* <TreeView /> */}
|
||||
</div>
|
||||
</LexicalComposer>
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'
|
||||
import { textToEditorState } from '../utils'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
|
||||
export const PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER = 'PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER'
|
||||
|
||||
const UpdateBlock = () => {
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [editor] = useLexicalComposerContext()
|
||||
|
||||
eventEmitter?.useSubscription((v: any) => {
|
||||
if (v.type === PROMPT_EDITOR_UPDATE_VALUE_BY_EVENT_EMITTER) {
|
||||
const editorState = editor.parseEditorState(textToEditorState(v.payload))
|
||||
editor.setEditorState(editorState)
|
||||
}
|
||||
})
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
export default UpdateBlock
|
||||
@ -838,7 +838,7 @@ const StepTwo = ({
|
||||
{!isSetting
|
||||
? (
|
||||
<div className='flex items-center mt-8 py-2'>
|
||||
<Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.lastStep')}</Button>
|
||||
<Button onClick={() => onStepChange && onStepChange(-1)}>{t('datasetCreation.stepTwo.previousStep')}</Button>
|
||||
<div className={s.divider} />
|
||||
<Button loading={isCreating} type='primary' onClick={createHandle}>{t('datasetCreation.stepTwo.nextStep')}</Button>
|
||||
</div>
|
||||
|
||||
@ -427,6 +427,53 @@ Chat applications support session persistence, allowing previous chat history to
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
title='next suggested questions'
|
||||
name='#suggested'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
Get next questions suggestions for the current message
|
||||
|
||||
### Path Params
|
||||
|
||||
<Properties>
|
||||
<Property name='message_id' type='string' key='message_id'>
|
||||
Message ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/messages/{message_id}/suggested" targetCode={`curl --location --request GET '${props.appDetail.api_base_url}/messages/{message_id}/suggested \\\n--header 'Authorization: Bearer ENTER-YOUR-SECRET-KEY' \\\n--header 'Content-Type: application/json'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.appDetail.api_base_url}/messages/{message_id}/suggested' \
|
||||
--header 'Authorization: Bearer ENTER-YOUR-SECRET-KEY' \
|
||||
--header 'Content-Type: application/json' \
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success",
|
||||
"data": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages'
|
||||
method='GET'
|
||||
|
||||
@ -442,6 +442,55 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/{message_id}/suggested'
|
||||
method='GET'
|
||||
title='获取下一轮建议问题列表'
|
||||
name='#suggested'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
获取下一轮建议问题列表。
|
||||
|
||||
### Path Params
|
||||
|
||||
<Properties>
|
||||
<Property name='message_id' type='string' key='message_id'>
|
||||
Message ID
|
||||
</Property>
|
||||
</Properties>
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="GET" label="/messages/{message_id}/suggested" targetCode={`curl --location --request GET '${props.appDetail.api_base_url}/messages/{message_id}/suggested \\\n--header 'Authorization: Bearer ENTER-YOUR-SECRET-KEY' \\\n--header 'Content-Type: application/json'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl --location --request GET '${props.appDetail.api_base_url}/messages/{message_id}/suggested' \
|
||||
--header 'Authorization: Bearer ENTER-YOUR-SECRET-KEY' \
|
||||
--header 'Content-Type: application/json' \
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success",
|
||||
"data": [
|
||||
"a",
|
||||
"b",
|
||||
"c"
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages'
|
||||
method='GET'
|
||||
|
||||
@ -71,10 +71,14 @@ export default function AccountPage() {
|
||||
showErrorMessage(t('login.error.passwordEmpty'))
|
||||
return false
|
||||
}
|
||||
if (!validPassword.test(password))
|
||||
if (!validPassword.test(password)) {
|
||||
showErrorMessage(t('login.error.passwordInvalid'))
|
||||
if (password !== confirmPassword)
|
||||
return false
|
||||
}
|
||||
if (password !== confirmPassword) {
|
||||
showErrorMessage(t('common.account.notEqual'))
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -18,7 +18,7 @@ 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 } from '@/service/tools'
|
||||
import { createCustomCollection, fetchCollectionList as doFetchCollectionList, fetchBuiltInToolList, fetchCustomToolList, fetchModelToolList } from '@/service/tools'
|
||||
import type { AgentTool } from '@/types/app'
|
||||
|
||||
type Props = {
|
||||
@ -89,9 +89,11 @@ const Tools: FC<Props> = ({
|
||||
const showCollectionList = (() => {
|
||||
let typeFilteredList: Collection[] = []
|
||||
if (collectionType === CollectionType.all)
|
||||
typeFilteredList = collectionList
|
||||
else
|
||||
typeFilteredList = collectionList.filter(item => item.type === collectionType)
|
||||
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))
|
||||
|
||||
@ -122,6 +124,10 @@ const Tools: FC<Props> = ({
|
||||
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)
|
||||
@ -130,7 +136,7 @@ const Tools: FC<Props> = ({
|
||||
catch (e) { }
|
||||
setIsDetailLoading(false)
|
||||
})()
|
||||
}, [currCollection?.name])
|
||||
}, [currCollection?.name, currCollection?.type])
|
||||
|
||||
const [isShowEditCollectionToolModal, setIsShowEditCollectionToolModal] = useState(false)
|
||||
const handleCreateToolCollection = () => {
|
||||
@ -197,7 +203,7 @@ const Tools: FC<Props> = ({
|
||||
(showCollectionList.length > 0 || !query)
|
||||
? <ToolNavList
|
||||
className='mt-2 grow height-0 overflow-y-auto'
|
||||
currentName={currCollection?.name || ''}
|
||||
currentIndex={currCollectionIndex || 0}
|
||||
list={showCollectionList}
|
||||
onChosen={setCurrCollectionIndex}
|
||||
/>
|
||||
|
||||
@ -29,9 +29,8 @@ const Header: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
const isInToolsPage = loc === LOC.tools
|
||||
const isInDebugPage = !isInToolsPage
|
||||
const needAuth = collection?.allow_delete
|
||||
|
||||
// const isBuiltIn = collection.type === CollectionType.builtIn
|
||||
const needAuth = collection?.allow_delete || collection?.type === CollectionType.model
|
||||
const isAuthed = collection.is_team_authorization
|
||||
return (
|
||||
<div className={cn(isInToolsPage ? 'py-4 px-6' : 'py-[11px] pl-4 pr-3', 'flex justify-between items-start border-b border-gray-200')}>
|
||||
@ -50,10 +49,13 @@ const Header: FC<Props> = ({
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
{collection.type === CollectionType.builtIn && needAuth && (
|
||||
{(collection.type === CollectionType.builtIn || collection.type === CollectionType.model) && needAuth && (
|
||||
<div
|
||||
className={cn('cursor-pointer', 'ml-1 shrink-0 flex items-center h-8 border border-gray-200 rounded-lg px-3 space-x-2 shadow-xs')}
|
||||
onClick={() => onShowAuth()}
|
||||
onClick={() => {
|
||||
if (collection.type === CollectionType.builtIn || collection.type === CollectionType.model)
|
||||
onShowAuth()
|
||||
}}
|
||||
>
|
||||
<div className={cn(isAuthed ? 'border-[#12B76A] bg-[#32D583]' : 'border-gray-400 bg-gray-300', 'rounded h-2 w-2 border')}></div>
|
||||
<div className='leading-5 text-sm font-medium text-gray-700'>{t(`tools.auth.${isAuthed ? 'authorized' : 'unauthorized'}`)}</div>
|
||||
|
||||
@ -8,6 +8,7 @@ import type { Collection, CustomCollectionBackend, Tool } from '../types'
|
||||
import Loading from '../../base/loading'
|
||||
import { ArrowNarrowRight } from '../../base/icons/src/vender/line/arrows'
|
||||
import Toast from '../../base/toast'
|
||||
import { ConfigurateMethodEnum } from '../../header/account-setting/model-provider-page/declarations'
|
||||
import Header from './header'
|
||||
import Item from './item'
|
||||
import AppIcon from '@/app/components/base/app-icon'
|
||||
@ -16,6 +17,8 @@ import { fetchCustomCollection, removeBuiltInToolCredential, removeCustomCollect
|
||||
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
|
||||
import type { AgentTool } from '@/types/app'
|
||||
import { MAX_TOOLS_NUM } from '@/config'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
|
||||
type Props = {
|
||||
collection: Collection | null
|
||||
@ -42,9 +45,32 @@ const ToolList: FC<Props> = ({
|
||||
const { t } = useTranslation()
|
||||
const isInToolsPage = loc === LOC.tools
|
||||
const isBuiltIn = collection?.type === CollectionType.builtIn
|
||||
const isModel = collection?.type === CollectionType.model
|
||||
const needAuth = collection?.allow_delete
|
||||
|
||||
const { setShowModelModal } = useModalContext()
|
||||
const [showSettingAuth, setShowSettingAuth] = useState(false)
|
||||
const { modelProviders: providers } = useProviderContext()
|
||||
const showSettingAuthModal = () => {
|
||||
if (isModel) {
|
||||
const provider = providers.find(item => item.provider === collection?.id)
|
||||
if (provider) {
|
||||
setShowModelModal({
|
||||
payload: {
|
||||
currentProvider: provider,
|
||||
currentConfigurateMethod: ConfigurateMethodEnum.predefinedModel,
|
||||
currentCustomConfigrationModelFixedFields: undefined,
|
||||
},
|
||||
onSaveCallback: () => {
|
||||
onRefreshData()
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
else {
|
||||
setShowSettingAuth(true)
|
||||
}
|
||||
}
|
||||
|
||||
const [customCollection, setCustomCollection] = useState<CustomCollectionBackend | null>(null)
|
||||
useEffect(() => {
|
||||
@ -116,7 +142,7 @@ const ToolList: FC<Props> = ({
|
||||
icon={icon}
|
||||
collection={collection}
|
||||
loc={loc}
|
||||
onShowAuth={() => setShowSettingAuth(true)}
|
||||
onShowAuth={() => showSettingAuthModal()}
|
||||
onShowEditCustomCollection={() => setIsShowEditCustomCollectionModal(true)}
|
||||
/>
|
||||
<div className={cn(isInToolsPage ? 'px-6 pt-4' : 'px-4 pt-3')}>
|
||||
@ -124,12 +150,12 @@ const ToolList: FC<Props> = ({
|
||||
<div className=''>{t('tools.includeToolNum', {
|
||||
num: list.length,
|
||||
})}</div>
|
||||
{needAuth && isBuiltIn && !collection.is_team_authorization && (
|
||||
{needAuth && (isBuiltIn || isModel) && !collection.is_team_authorization && (
|
||||
<>
|
||||
<div>·</div>
|
||||
<div
|
||||
className='flex items-center text-[#155EEF] cursor-pointer'
|
||||
onClick={() => setShowSettingAuth(true)}
|
||||
onClick={() => showSettingAuthModal()}
|
||||
>
|
||||
<div>{t('tools.auth.setup')}</div>
|
||||
<ArrowNarrowRight className='ml-0.5 w-3 h-3' />
|
||||
@ -149,7 +175,7 @@ const ToolList: FC<Props> = ({
|
||||
collection={collection}
|
||||
isInToolsPage={isInToolsPage}
|
||||
isToolNumMax={(addedTools?.length || 0) >= MAX_TOOLS_NUM}
|
||||
added={!!addedTools?.find(v => v.provider_id === collection.id && v.tool_name === item.name)}
|
||||
added={!!addedTools?.find(v => v.provider_id === collection.id && v.provider_type === collection.type && v.tool_name === item.name)}
|
||||
onAdd={!isInToolsPage ? tool => onAddTool?.(collection as Collection, tool) : undefined}
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -35,6 +35,7 @@ const Item: FC<Props> = ({
|
||||
const language = getLanguage(locale)
|
||||
|
||||
const isBuiltIn = collection.type === CollectionType.builtIn
|
||||
const isModel = collection.type === CollectionType.model
|
||||
const canShowDetail = isInToolsPage
|
||||
const [showDetail, setShowDetail] = useState(false)
|
||||
const addBtn = <Button className='shrink-0 flex items-center h-7 !px-3 !text-xs !font-medium !text-gray-700' disabled={added || !collection.is_team_authorization} onClick={() => onAdd?.(payload)}>{t(`common.operation.${added ? 'added' : 'add'}`)}</Button>
|
||||
@ -73,6 +74,7 @@ const Item: FC<Props> = ({
|
||||
setShowDetail(false)
|
||||
}}
|
||||
isBuiltIn={isBuiltIn}
|
||||
isModel={isModel}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -6,21 +6,21 @@ import Item from './item'
|
||||
import type { Collection } from '@/app/components/tools/types'
|
||||
type Props = {
|
||||
className?: string
|
||||
currentName: string
|
||||
currentIndex: number
|
||||
list: Collection[]
|
||||
onChosen: (index: number) => void
|
||||
}
|
||||
|
||||
const ToolNavList: FC<Props> = ({
|
||||
className,
|
||||
currentName,
|
||||
currentIndex,
|
||||
list,
|
||||
onChosen,
|
||||
}) => {
|
||||
return (
|
||||
<div className={cn(className)}>
|
||||
{list.map((item, index) => (
|
||||
<Item isCurrent={item.name === currentName} key={item.name} payload={item} onClick={() => onChosen(index)}></Item>
|
||||
<Item isCurrent={index === currentIndex} key={index} payload={item} onClick={() => onChosen(index)}></Item>
|
||||
))}
|
||||
</div>
|
||||
)
|
||||
|
||||
@ -26,6 +26,7 @@ export enum CollectionType {
|
||||
all = 'all',
|
||||
builtIn = 'builtin',
|
||||
custom = 'api',
|
||||
model = 'model',
|
||||
}
|
||||
|
||||
export type Emoji = {
|
||||
|
||||
@ -89,7 +89,7 @@ const translation = {
|
||||
other: 'and other ',
|
||||
fileUnit: ' files',
|
||||
notionUnit: ' pages',
|
||||
lastStep: 'Last step',
|
||||
previousStep: 'Previous step',
|
||||
nextStep: 'Save & Process',
|
||||
save: 'Save & Process',
|
||||
cancel: 'Cancel',
|
||||
|
||||
@ -89,7 +89,7 @@ const translation = {
|
||||
other: 'その他',
|
||||
fileUnit: 'ファイル',
|
||||
notionUnit: 'ページ',
|
||||
lastStep: '最後のステップ',
|
||||
previousStep: '前のステップ',
|
||||
nextStep: '保存して処理',
|
||||
save: '保存して処理',
|
||||
cancel: 'キャンセル',
|
||||
|
||||
@ -89,7 +89,7 @@ const translation = {
|
||||
other: 'e outros ',
|
||||
fileUnit: ' arquivos',
|
||||
notionUnit: ' páginas',
|
||||
lastStep: 'Última etapa',
|
||||
previousStep: 'Passo anterior',
|
||||
nextStep: 'Salvar e Processar',
|
||||
save: 'Salvar e Processar',
|
||||
cancel: 'Cancelar',
|
||||
|
||||
@ -89,7 +89,7 @@ const translation = {
|
||||
other: ' та інші ',
|
||||
fileUnit: ' файли',
|
||||
notionUnit: ' сторінки',
|
||||
lastStep: 'Попередній крок',
|
||||
previousStep: 'Попередній крок',
|
||||
nextStep: 'Зберегти та обробити',
|
||||
save: 'Зберегти та обробити',
|
||||
cancel: 'Скасувати',
|
||||
|
||||
@ -89,7 +89,7 @@ const translation = {
|
||||
other: '和其他 ',
|
||||
fileUnit: ' 个文件',
|
||||
notionUnit: ' 个页面',
|
||||
lastStep: '上一步',
|
||||
previousStep: '上一步',
|
||||
nextStep: '保存并处理',
|
||||
save: '保存并处理',
|
||||
cancel: '取消',
|
||||
|
||||
@ -12,6 +12,11 @@ export const fetchBuiltInToolList = (collectionName: string) => {
|
||||
export const fetchCustomToolList = (collectionName: string) => {
|
||||
return get<Tool[]>(`/workspaces/current/tool-provider/api/tools?provider=${collectionName}`)
|
||||
}
|
||||
|
||||
export const fetchModelToolList = (collectionName: string) => {
|
||||
return get<Tool[]>(`/workspaces/current/tool-provider/model/tools?provider=${collectionName}`)
|
||||
}
|
||||
|
||||
export const fetchBuiltInToolCredentialSchema = (collectionName: string) => {
|
||||
return get<ToolCredential[]>(`/workspaces/current/tool-provider/builtin/${collectionName}/credentials_schema`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user