merge main

This commit is contained in:
AkaraChen
2024-12-05 10:57:27 +08:00
358 changed files with 9363 additions and 2178 deletions

View File

@ -1,23 +1,27 @@
'use client'
import { useContextSelector } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import style from '../list.module.css'
import Apps from './Apps'
import classNames from '@/utils/classnames'
import { getLocaleOnServer, useTranslation as translate } from '@/i18n/server'
import AppContext from '@/context/app-context'
import { LicenseStatus } from '@/types/feature'
const AppList = async () => {
const locale = getLocaleOnServer()
const { t } = await translate(locale, 'app')
const AppList = () => {
const { t } = useTranslation()
const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)
return (
<div className='relative flex flex-col overflow-y-auto bg-gray-100 shrink-0 h-0 grow'>
<Apps />
<footer className='px-12 py-6 grow-0 shrink-0'>
<h3 className='text-xl font-semibold leading-tight text-gradient'>{t('join')}</h3>
<p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('communityIntro')}</p>
{systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'>
<h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3>
<p className='mt-1 text-sm font-normal leading-tight text-gray-700'>{t('app.communityIntro')}</p>
<div className='flex items-center gap-2 mt-3'>
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://github.com/langgenius/dify'><span className={classNames(style.socialMediaIcon, style.githubIcon)} /></a>
<a className={style.socialMediaLink} target='_blank' rel='noopener noreferrer' href='https://discord.gg/FngNHpbcY7'><span className={classNames(style.socialMediaIcon, style.discordIcon)} /></a>
</div>
</footer>
</footer>}
</div >
)
}

View File

@ -2,19 +2,17 @@
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Pagination } from 'react-headless-pagination'
import { useDebounce } from 'ahooks'
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
import Toast from '../../base/toast'
import Filter from './filter'
import type { QueryParam } from './filter'
import List from './list'
import EmptyElement from './empty-element'
import HeaderOpts from './header-opts'
import s from './style.module.css'
import { AnnotationEnableStatus, type AnnotationItem, type AnnotationItemBasic, JobStatus } from './type'
import ViewAnnotationModal from './view-annotation-modal'
import cn from '@/utils/classnames'
import Pagination from '@/app/components/base/pagination'
import Switch from '@/app/components/base/switch'
import { addAnnotation, delAnnotation, fetchAnnotationConfig as doFetchAnnotationConfig, editAnnotation, fetchAnnotationList, queryAnnotationJobStatus, updateAnnotationScore, updateAnnotationStatus } from '@/service/annotation'
import Loading from '@/app/components/base/loading'
@ -69,9 +67,10 @@ const Annotation: FC<Props> = ({
const [queryParams, setQueryParams] = useState<QueryParam>({})
const [currPage, setCurrPage] = React.useState<number>(0)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
const [limit, setLimit] = React.useState<number>(APP_PAGE_LIMIT)
const query = {
page: currPage + 1,
limit: APP_PAGE_LIMIT,
limit,
keyword: debouncedQueryParams.keyword || '',
}
@ -228,35 +227,12 @@ const Annotation: FC<Props> = ({
{/* Show Pagination only if the total is more than the limit */}
{(total && total > APP_PAGE_LIMIT)
? <Pagination
className="flex items-center w-full h-10 text-sm select-none mt-8"
currentPage={currPage}
edgePageCount={2}
middlePagesSiblingCount={1}
setCurrentPage={setCurrPage}
totalPages={Math.ceil(total / APP_PAGE_LIMIT)}
truncableClassName="w-8 px-0.5 text-center"
truncableText="..."
>
<Pagination.PrevButton
disabled={currPage === 0}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
<ArrowLeftIcon className="mr-3 h-3 w-3" />
{t('appLog.table.pagination.previous')}
</Pagination.PrevButton>
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
<Pagination.PageButton
activeClassName="bg-primary-50 dark:bg-opacity-0 text-primary-600 dark:text-white"
className="flex items-center justify-center h-8 w-8 rounded-full cursor-pointer"
inactiveClassName="text-gray-500"
/>
</div>
<Pagination.NextButton
disabled={currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
{t('appLog.table.pagination.next')}
<ArrowRightIcon className="ml-3 h-3 w-3" />
</Pagination.NextButton>
</Pagination>
current={currPage}
onChange={setCurrPage}
total={total}
limit={limit}
onLimitChange={setLimit}
/>
: null}
{isShowViewModal && (

View File

@ -1,3 +0,0 @@
.pagination li {
list-style: none;
}

View File

@ -2,13 +2,12 @@
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { Pagination } from 'react-headless-pagination'
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
import EditItem, { EditItemType } from '../edit-annotation-modal/edit-item'
import type { AnnotationItem, HitHistoryItem } from '../type'
import s from './style.module.css'
import HitHistoryNoData from './hit-history-no-data'
import cn from '@/utils/classnames'
import Pagination from '@/app/components/base/pagination'
import Drawer from '@/app/components/base/drawer-plus'
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
import Confirm from '@/app/components/base/confirm'
@ -150,35 +149,10 @@ const ViewAnnotationModal: FC<Props> = ({
</table>
{(total && total > APP_PAGE_LIMIT)
? <Pagination
className="flex items-center w-full h-10 text-sm select-none mt-8"
currentPage={currPage}
edgePageCount={2}
middlePagesSiblingCount={1}
setCurrentPage={setCurrPage}
totalPages={Math.ceil(total / APP_PAGE_LIMIT)}
truncatableClassName="w-8 px-0.5 text-center"
truncatableText="..."
>
<Pagination.PrevButton
disabled={currPage === 0}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
<ArrowLeftIcon className="mr-3 h-3 w-3" />
{t('appLog.table.pagination.previous')}
</Pagination.PrevButton>
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
<Pagination.PageButton
activeClassName="bg-primary-50 dark:bg-opacity-0 text-primary-600 dark:text-white"
className="flex items-center justify-center h-8 w-8 rounded-full cursor-pointer"
inactiveClassName="text-gray-500"
/>
</div>
<Pagination.NextButton
disabled={currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
{t('appLog.table.pagination.next')}
<ArrowRightIcon className="ml-3 h-3 w-3" />
</Pagination.NextButton>
</Pagination>
current={currPage}
onChange={setCurrPage}
total={total}
/>
: null}
</div>

View File

@ -29,6 +29,7 @@ import { useAppContext } from '@/context/app-context'
import { ModelFeatureEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useFeatures } from '@/app/components/base/features/hooks'
import type { InputForm } from '@/app/components/base/chat/chat/type'
import { getLastAnswer } from '@/app/components/base/chat/utils'
type ChatItemProps = {
modelAndParameter: ModelAndParameter
@ -101,7 +102,7 @@ const ChatItem: FC<ChatItemProps> = ({
query: message,
inputs,
model_config: configData,
parent_message_id: chatListRef.current.at(-1)?.id || null,
parent_message_id: getLastAnswer(chatListRef.current)?.id || null,
}
if ((config.file_upload as any).enabled && files?.length && supportVision)

View File

@ -22,7 +22,7 @@ import { getNewVar } from '@/utils/var'
import { varHighlightHTML } from '@/app/components/app/configuration/base/var-highlight'
import Toast from '@/app/components/base/toast'
const MAX_QUESTION_NUM = 5
const MAX_QUESTION_NUM = 10
export type IOpeningStatementProps = {
value: string

View File

@ -52,7 +52,7 @@ const LogAnnotation: FC<Props> = ({
options={options}
/>
)}
<div className={cn('grow', appDetail.mode !== 'workflow' && 'mt-3')}>
<div className={cn('grow h-0', appDetail.mode !== 'workflow' && 'mt-3')}>
{pageType === PageType.log && appDetail.mode !== 'workflow' && (<Log appDetail={appDetail} />)}
{pageType === PageType.annotation && (<Annotation appDetail={appDetail} />)}
{pageType === PageType.log && appDetail.mode === 'workflow' && (<WorkflowLog appDetail={appDetail} />)}

View File

@ -2,17 +2,15 @@
import type { FC, SVGProps } from 'react'
import React, { useState } from 'react'
import useSWR from 'swr'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { Pagination } from 'react-headless-pagination'
import { useDebounce } from 'ahooks'
import { omit } from 'lodash-es'
import dayjs from 'dayjs'
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
import { Trans, useTranslation } from 'react-i18next'
import Link from 'next/link'
import List from './list'
import Filter, { TIME_PERIOD_MAPPING } from './filter'
import s from './style.module.css'
import Pagination from '@/app/components/base/pagination'
import Loading from '@/app/components/base/loading'
import { fetchChatConversations, fetchCompletionConversations } from '@/service/log'
import { APP_PAGE_LIMIT } from '@/config'
@ -60,6 +58,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
sort_by: '-created_at',
})
const [currPage, setCurrPage] = React.useState<number>(0)
const [limit, setLimit] = React.useState<number>(APP_PAGE_LIMIT)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
// Get the app type first
@ -67,7 +66,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
const query = {
page: currPage + 1,
limit: APP_PAGE_LIMIT,
limit,
...((debouncedQueryParams.period !== '9')
? {
start: dayjs().subtract(TIME_PERIOD_MAPPING[debouncedQueryParams.period].value, 'day').startOf('day').format('YYYY-MM-DD HH:mm'),
@ -102,9 +101,9 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
const total = isChatMode ? chatConversations?.total : completionConversations?.total
return (
<div className='flex flex-col h-full'>
<p className='text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
<div className='flex flex-col py-4 flex-1'>
<div className='grow flex flex-col h-full'>
<p className='shrink-0 text-text-tertiary system-sm-regular'>{t('appLog.description')}</p>
<div className='grow max-h-[calc(100%-16px)] flex flex-col py-4 flex-1'>
<Filter isChatMode={isChatMode} appId={appDetail.id} queryParams={queryParams} setQueryParams={setQueryParams} />
{total === undefined
? <Loading type='app' />
@ -115,35 +114,12 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
{/* Show Pagination only if the total is more than the limit */}
{(total && total > APP_PAGE_LIMIT)
? <Pagination
className="flex items-center w-full h-10 text-sm select-none mt-8"
currentPage={currPage}
edgePageCount={2}
middlePagesSiblingCount={1}
setCurrentPage={setCurrPage}
totalPages={Math.ceil(total / APP_PAGE_LIMIT)}
truncableClassName="w-8 px-0.5 text-center"
truncableText="..."
>
<Pagination.PrevButton
disabled={currPage === 0}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
<ArrowLeftIcon className="mr-3 h-3 w-3" />
{t('appLog.table.pagination.previous')}
</Pagination.PrevButton>
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
<Pagination.PageButton
activeClassName="bg-primary-50 dark:bg-opacity-0 text-primary-600 dark:text-white"
className="flex items-center justify-center h-8 w-8 rounded-full cursor-pointer"
inactiveClassName="text-gray-500"
/>
</div>
<Pagination.NextButton
disabled={currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
{t('appLog.table.pagination.next')}
<ArrowRightIcon className="ml-3 h-3 w-3" />
</Pagination.NextButton>
</Pagination>
current={currPage}
onChange={setCurrPage}
total={total}
limit={limit}
onLimitChange={setLimit}
/>
: null}
</div>
</div>

View File

@ -318,7 +318,7 @@ function DetailPanel({ detail, onFeedback }: IDetailPanel) {
const targetTone = TONE_LIST.find((item: any) => {
let res = true
validatedParams.forEach((param) => {
res = item.config?.[param] === detail.model_config?.configs?.completion_params?.[param]
res = item.config?.[param] === detail?.model_config.model?.completion_params?.[param]
})
return res
})?.name ?? 'custom'

View File

@ -1,3 +0,0 @@
.pagination li {
list-style: none;
}

View File

@ -3,14 +3,12 @@ import type { FC, SVGProps } from 'react'
import React, { useState } from 'react'
import useSWR from 'swr'
import { usePathname } from 'next/navigation'
import { Pagination } from 'react-headless-pagination'
import { useDebounce } from 'ahooks'
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
import { Trans, useTranslation } from 'react-i18next'
import Link from 'next/link'
import List from './list'
import Filter from './filter'
import s from './style.module.css'
import Pagination from '@/app/components/base/pagination'
import Loading from '@/app/components/base/loading'
import { fetchWorkflowLogs } from '@/service/log'
import { APP_PAGE_LIMIT } from '@/config'
@ -53,10 +51,11 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
const [queryParams, setQueryParams] = useState<QueryParam>({ status: 'all' })
const [currPage, setCurrPage] = React.useState<number>(0)
const debouncedQueryParams = useDebounce(queryParams, { wait: 500 })
const [limit, setLimit] = React.useState<number>(APP_PAGE_LIMIT)
const query = {
page: currPage + 1,
limit: APP_PAGE_LIMIT,
limit,
...(debouncedQueryParams.status !== 'all' ? { status: debouncedQueryParams.status } : {}),
...(debouncedQueryParams.keyword ? { keyword: debouncedQueryParams.keyword } : {}),
}
@ -77,7 +76,7 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
<div className='flex flex-col h-full'>
<h1 className='text-text-primary system-xl-semibold'>{t('appLog.workflowTitle')}</h1>
<p className='text-text-tertiary system-sm-regular'>{t('appLog.workflowSubtitle')}</p>
<div className='flex flex-col py-4 flex-1'>
<div className='flex flex-col py-4 flex-1 max-h-[calc(100%-16px)]'>
<Filter queryParams={queryParams} setQueryParams={setQueryParams} />
{/* workflow log */}
{total === undefined
@ -89,35 +88,12 @@ const Logs: FC<ILogsProps> = ({ appDetail }) => {
{/* Show Pagination only if the total is more than the limit */}
{(total && total > APP_PAGE_LIMIT)
? <Pagination
className="flex items-center w-full h-10 text-sm select-none mt-8"
currentPage={currPage}
edgePageCount={2}
middlePagesSiblingCount={1}
setCurrentPage={setCurrPage}
totalPages={Math.ceil(total / APP_PAGE_LIMIT)}
truncableClassName="w-8 px-0.5 text-center"
truncableText="..."
>
<Pagination.PrevButton
disabled={currPage === 0}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
<ArrowLeftIcon className="mr-3 h-3 w-3" />
{t('appLog.table.pagination.previous')}
</Pagination.PrevButton>
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
<Pagination.PageButton
activeClassName="bg-primary-50 dark:bg-opacity-0 text-primary-600 dark:text-white"
className="flex items-center justify-center h-8 w-8 rounded-full cursor-pointer"
inactiveClassName="text-gray-500"
/>
</div>
<Pagination.NextButton
disabled={currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${currPage === Math.ceil(total / APP_PAGE_LIMIT) - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600 dark:hover:text-gray-200'}`} >
{t('appLog.table.pagination.next')}
<ArrowRightIcon className="ml-3 h-3 w-3" />
</Pagination.NextButton>
</Pagination>
current={currPage}
onChange={setCurrPage}
total={total}
limit={limit}
onLimitChange={setLimit}
/>
: null}
</div>
</div>

View File

@ -2,9 +2,7 @@
import type { FC } from 'react'
import React, { useState } from 'react'
import { useTranslation } from 'react-i18next'
// import s from './style.module.css'
import DetailPanel from './detail'
import cn from '@/utils/classnames'
import type { WorkflowAppLogDetail, WorkflowLogsResponse } from '@/models/log'
import type { App } from '@/types/app'
import Loading from '@/app/components/base/loading'
@ -12,6 +10,7 @@ import Drawer from '@/app/components/base/drawer'
import Indicator from '@/app/components/header/indicator'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import useTimestamp from '@/hooks/use-timestamp'
import cn from '@/utils/classnames'
type ILogs = {
logs?: WorkflowLogsResponse

View File

@ -1,3 +0,0 @@
.pagination li {
list-style: none;
}

View File

@ -15,7 +15,7 @@ const Badge = ({
return (
<div
className={cn(
'inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep leading-3 text-text-tertiary',
'inline-flex items-center px-[5px] h-5 rounded-[5px] border border-divider-deep leading-3 text-text-tertiary',
uppercase ? 'system-2xs-medium-uppercase' : 'system-xs-medium',
className,
)}

View File

@ -3,17 +3,21 @@ import React from 'react'
import { type VariantProps, cva } from 'class-variance-authority'
import classNames from '@/utils/classnames'
const dividerVariants = cva(
'bg-divider-regular',
const dividerVariants = cva('',
{
variants: {
type: {
horizontal: 'w-full h-[0.5px] my-2',
horizontal: 'w-full h-[0.5px] my-2 ',
vertical: 'w-[1px] h-full mx-2',
},
bgStyle: {
gradient: 'bg-gradient-to-r from-divider-regular to-background-gradient-mask-transparent',
solid: 'bg-divider-regular',
},
},
defaultVariants: {
type: 'horizontal',
bgStyle: 'solid',
},
},
)
@ -23,9 +27,9 @@ export type DividerProps = {
style?: CSSProperties
} & VariantProps<typeof dividerVariants>
const Divider: FC<DividerProps> = ({ type, className = '', style }) => {
const Divider: FC<DividerProps> = ({ type, bgStyle, className = '', style }) => {
return (
<div className={classNames(dividerVariants({ type }), className)} style={style}></div>
<div className={classNames(dividerVariants({ type, bgStyle }), className)} style={style}></div>
)
}

View File

@ -22,7 +22,7 @@ type OpeningSettingModalProps = {
onAutoAddPromptVariable?: (variable: PromptVariable[]) => void
}
const MAX_QUESTION_NUM = 5
const MAX_QUESTION_NUM = 10
const OpeningSettingModal = ({
data,

View File

@ -84,7 +84,7 @@ const FileImageItem = ({
className='absolute bottom-0.5 right-0.5 flex items-center justify-center w-6 h-6 rounded-lg bg-components-actionbar-bg shadow-md'
onClick={(e) => {
e.stopPropagation()
downloadFile(url || '', name)
downloadFile(url || base64Url || '', name)
}}
>
<RiDownloadLine className='w-4 h-4 text-text-tertiary' />

View File

@ -80,7 +80,7 @@ const FileItem = ({
}
</div>
{
showDownloadAction && (
showDownloadAction && url && (
<ActionButton
size='m'
className='hidden group-hover/file-item:flex absolute -right-1 -top-1'

View File

@ -53,8 +53,7 @@ const ImageGallery: FC<Props> = ({
imagePreviewUrl && (
<ImagePreview
url={imagePreviewUrl}
onCancel={() => setImagePreviewUrl('')}
/>
onCancel={() => setImagePreviewUrl('')} title={''} />
)
}
</div>

View File

@ -64,7 +64,9 @@ const Input = ({
destructive && 'bg-components-input-bg-destructive border-components-input-border-destructive text-components-input-text-filled hover:bg-components-input-bg-destructive hover:border-components-input-border-destructive focus:bg-components-input-bg-destructive focus:border-components-input-border-destructive',
className,
)}
placeholder={placeholder ?? (showLeftIcon ? t('common.operation.search') ?? '' : t('common.placeholder.input'))}
placeholder={placeholder ?? (showLeftIcon
? (t('common.operation.search') || '')
: (t('common.placeholder.input') || ''))}
value={value}
onChange={onChange}
disabled={disabled}

View File

@ -0,0 +1,21 @@
type HorizontalLineProps = {
className?: string
}
const HorizontalLine = ({
className,
}: HorizontalLineProps) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="240" height="2" viewBox="0 0 240 2" fill="none" className={className}>
<path d="M0 1H240" stroke="url(#paint0_linear_8619_59125)"/>
<defs>
<linearGradient id="paint0_linear_8619_59125" x1="240" y1="9.99584" x2="3.95539e-05" y2="9.88094" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.01"/>
<stop offset="0.9031" stopColor="#101828" stopOpacity="0.04"/>
<stop offset="1" stopColor="white" stopOpacity="0.01"/>
</linearGradient>
</defs>
</svg>
)
}
export default HorizontalLine

View File

@ -0,0 +1,35 @@
import React from 'react'
import { Variable02 } from '../icons/src/vender/solid/development'
import VerticalLine from './vertical-line'
import HorizontalLine from './horizontal-line'
type ListEmptyProps = {
title?: string
description?: React.ReactNode
}
const ListEmpty = ({
title,
description,
}: ListEmptyProps) => {
return (
<div className='flex w-[320px] p-4 flex-col items-start gap-2 rounded-[10px] bg-workflow-process-bg'>
<div className='flex w-10 h-10 justify-center items-center gap-2 rounded-[10px]'>
<div className='flex relative p-1 justify-center items-center gap-2 grow self-stretch rounded-[10px]
border-[0.5px] border-components-card-border bg-components-card-bg shadow-lg'>
<Variable02 className='w-5 h-5 shrink-0 text-text-accent' />
<VerticalLine className='absolute -right-[1px] top-1/2 -translate-y-1/4'/>
<VerticalLine className='absolute -left-[1px] top-1/2 -translate-y-1/4'/>
<HorizontalLine className='absolute top-0 left-3/4 -translate-x-1/4 -translate-y-1/2'/>
<HorizontalLine className='absolute top-full left-3/4 -translate-x-1/4 -translate-y-1/2' />
</div>
</div>
<div className='flex flex-col items-start gap-1 self-stretch'>
<div className='text-text-secondary system-sm-medium'>{title}</div>
{description}
</div>
</div>
)
}
export default ListEmpty

View File

@ -0,0 +1,21 @@
type VerticalLineProps = {
className?: string
}
const VerticalLine = ({
className,
}: VerticalLineProps) => {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="2" height="132" viewBox="0 0 2 132" fill="none" className={className}>
<path d="M1 0L1 132" stroke="url(#paint0_linear_8619_59128)"/>
<defs>
<linearGradient id="paint0_linear_8619_59128" x1="-7.99584" y1="132" x2="-7.96108" y2="6.4974e-07" gradientUnits="userSpaceOnUse">
<stop stopColor="white" stopOpacity="0.01"/>
<stop offset="0.877606" stopColor="#101828" stopOpacity="0.04"/>
<stop offset="1" stopColor="white" stopOpacity="0.01"/>
</linearGradient>
</defs>
</svg>
)
}
export default VerticalLine

View File

@ -8,8 +8,7 @@ import RemarkGfm from 'remark-gfm'
import RehypeRaw from 'rehype-raw'
import SyntaxHighlighter from 'react-syntax-highlighter'
import { atelierHeathLight } from 'react-syntax-highlighter/dist/esm/styles/hljs'
import type { RefObject } from 'react'
import { Component, memo, useEffect, useMemo, useRef, useState } from 'react'
import { Component, memo, useMemo, useRef, useState } from 'react'
import type { CodeComponent } from 'react-markdown/lib/ast-to-react'
import cn from '@/utils/classnames'
import CopyBtn from '@/app/components/base/copy-btn'
@ -77,29 +76,6 @@ export function PreCode(props: { children: any }) {
)
}
// eslint-disable-next-line unused-imports/no-unused-vars
const useLazyLoad = (ref: RefObject<Element>): boolean => {
const [isIntersecting, setIntersecting] = useState<boolean>(false)
useEffect(() => {
const observer = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setIntersecting(true)
observer.disconnect()
}
})
if (ref.current)
observer.observe(ref.current)
return () => {
observer.disconnect()
}
}, [ref])
return isIntersecting
}
// **Add code block
// Avoid error #185 (Maximum update depth exceeded.
// This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate.
@ -123,7 +99,7 @@ const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }
try {
return JSON.parse(String(children).replace(/\n$/, ''))
}
catch (error) {}
catch (error) { }
}
return JSON.parse('{"title":{"text":"ECharts error - Wrong JSON format."}}')
}, [language, children])
@ -181,7 +157,7 @@ const CodeBlock: CodeComponent = memo(({ inline, className, children, ...props }
>
<div className='text-[13px] text-gray-500 font-normal'>{languageShowName}</div>
<div style={{ display: 'flex' }}>
{(['mermaid', 'svg']).includes(language!) && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG}/>}
{(['mermaid', 'svg']).includes(language!) && <SVGBtn isSVG={isSVG} setIsSVG={setIsSVG} />}
<CopyBtn
className='mr-1'
value={String(children).replace(/\n$/, '')}
@ -261,7 +237,7 @@ export function Markdown(props: { content: string; className?: string }) {
() => {
return (tree) => {
const iterate = (node: any) => {
if (node.type === 'element' && !node.properties?.src && node.properties?.ref && node.properties.ref.startsWith('{') && node.properties.ref.endsWith('}'))
if (node.type === 'element' && node.properties?.ref)
delete node.properties.ref
if (node.children)

View File

@ -0,0 +1,95 @@
import React, { useCallback } from 'react'
import type { IPaginationProps, IUsePagination } from './type'
const usePagination = ({
currentPage,
setCurrentPage,
truncableText = '...',
truncableClassName = '',
totalPages,
edgePageCount,
middlePagesSiblingCount,
}: IPaginationProps): IUsePagination => {
const pages = Array(totalPages)
.fill(0)
.map((_, i) => i + 1)
const hasPreviousPage = currentPage > 1
const hasNextPage = currentPage < totalPages
const isReachedToFirst = currentPage <= middlePagesSiblingCount
const isReachedToLast = currentPage + middlePagesSiblingCount >= totalPages
const middlePages = React.useMemo(() => {
const middlePageCount = middlePagesSiblingCount * 2 + 1
if (isReachedToFirst)
return pages.slice(0, middlePageCount)
if (isReachedToLast)
return pages.slice(-middlePageCount)
return pages.slice(
currentPage - middlePagesSiblingCount,
currentPage + middlePagesSiblingCount + 1,
)
}, [currentPage, isReachedToFirst, isReachedToLast, middlePagesSiblingCount, pages])
const getAllPreviousPages = useCallback(() => {
return pages.slice(0, middlePages[0] - 1)
}, [middlePages, pages])
const previousPages = React.useMemo(() => {
if (isReachedToFirst || getAllPreviousPages().length < 1)
return []
return pages
.slice(0, edgePageCount)
.filter(p => !middlePages.includes(p))
}, [edgePageCount, getAllPreviousPages, isReachedToFirst, middlePages, pages])
const getAllNextPages = React.useMemo(() => {
return pages.slice(
middlePages[middlePages.length - 1],
pages[pages.length],
)
}, [pages, middlePages])
const nextPages = React.useMemo(() => {
if (isReachedToLast)
return []
if (getAllNextPages.length < 1)
return []
return pages
.slice(pages.length - edgePageCount, pages.length)
.filter(p => !middlePages.includes(p))
}, [edgePageCount, getAllNextPages.length, isReachedToLast, middlePages, pages])
const isPreviousTruncable = React.useMemo(() => {
// Is truncable if first value of middlePage is larger than last value of previousPages
return middlePages[0] > previousPages[previousPages.length - 1] + 1
}, [previousPages, middlePages])
const isNextTruncable = React.useMemo(() => {
// Is truncable if last value of middlePage is larger than first value of previousPages
return middlePages[middlePages.length - 1] + 1 < nextPages[0]
}, [nextPages, middlePages])
return {
currentPage,
setCurrentPage,
truncableText,
truncableClassName,
pages,
hasPreviousPage,
hasNextPage,
previousPages,
isPreviousTruncable,
middlePages,
isNextTruncable,
nextPages,
}
}
export default usePagination

View File

@ -1,50 +1,165 @@
import type { FC } from 'react'
import React from 'react'
import { Pagination } from 'react-headless-pagination'
import { ArrowLeftIcon, ArrowRightIcon } from '@heroicons/react/24/outline'
import { useTranslation } from 'react-i18next'
import s from './style.module.css'
import { RiArrowLeftLine, RiArrowRightLine } from '@remixicon/react'
import { useDebounceFn } from 'ahooks'
import { Pagination } from './pagination'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
import cn from '@/utils/classnames'
type Props = {
className?: string
current: number
onChange: (cur: number) => void
total: number
limit?: number
onLimitChange?: (limit: number) => void
}
const CustomizedPagination: FC<Props> = ({ current, onChange, total, limit = 10 }) => {
const CustomizedPagination: FC<Props> = ({
className,
current,
onChange,
total,
limit = 10,
onLimitChange,
}) => {
const { t } = useTranslation()
const totalPages = Math.ceil(total / limit)
const inputRef = React.useRef<HTMLDivElement>(null)
const [showInput, setShowInput] = React.useState(false)
const [inputValue, setInputValue] = React.useState<string | number>(current + 1)
const [showPerPageTip, setShowPerPageTip] = React.useState(false)
const { run: handlePaging } = useDebounceFn((value: string) => {
if (parseInt(value) > totalPages) {
setInputValue(totalPages)
onChange(totalPages - 1)
setShowInput(false)
return
}
if (parseInt(value) < 1) {
setInputValue(1)
onChange(0)
setShowInput(false)
return
}
onChange(parseInt(value) - 1)
setInputValue(parseInt(value))
setShowInput(false)
}, { wait: 500 })
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value
if (!value)
return setInputValue('')
if (isNaN(parseInt(value)))
return setInputValue('')
setInputValue(parseInt(value))
handlePaging(value)
}
return (
<Pagination
className="flex items-center w-full h-10 text-sm select-none mt-8"
className={cn('flex items-center w-full px-6 py-3 select-none', className)}
currentPage={current}
edgePageCount={2}
middlePagesSiblingCount={1}
setCurrentPage={onChange}
totalPages={totalPages}
truncatableClassName="w-8 px-0.5 text-center"
truncatableText="..."
truncableClassName='flex items-center justify-center w-8 px-1 py-2 system-sm-medium text-text-tertiary'
truncableText='...'
>
<Pagination.PrevButton
disabled={current === 0}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${current === 0 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600'}`} >
<ArrowLeftIcon className="mr-3 h-3 w-3" />
{t('appLog.table.pagination.previous')}
</Pagination.PrevButton>
<div className={`flex items-center justify-center flex-grow ${s.pagination}`}>
<div className='flex items-center gap-0.5 p-0.5 rounded-[10px] bg-background-section-burn'>
<Pagination.PrevButton
as={<div></div>}
disabled={current === 0}
>
<Button
variant='secondary'
className='w-7 h-7 px-1.5'
disabled={current === 0}
>
<RiArrowLeftLine className='h-4 w-4' />
</Button>
</Pagination.PrevButton>
{!showInput && (
<div
ref={inputRef}
className='flex items-center gap-0.5 px-2 py-1.5 rounded-lg hover:bg-state-base-hover-alt hover:cursor-text'
onClick={() => setShowInput(true)}
>
<div className='system-xs-medium text-text-secondary'>{current + 1}</div>
<div className='system-xs-medium text-text-quaternary'>/</div>
<div className='system-xs-medium text-text-secondary'>{totalPages}</div>
</div>
)}
{showInput && (
<Input
styleCss={{
height: '28px',
width: `${inputRef.current?.clientWidth}px`,
}}
placeholder=''
autoFocus
value={inputValue}
onChange={handleInputChange}
onBlur={() => setShowInput(false)}
/>
)}
<Pagination.NextButton
as={<div></div>}
disabled={current === totalPages - 1}
>
<Button
variant='secondary'
className='w-7 h-7 px-1.5'
disabled={current === totalPages - 1}
>
<RiArrowRightLine className='h-4 w-4' />
</Button>
</Pagination.NextButton>
</div>
<div className={cn('grow flex items-center justify-center gap-1 list-none')}>
<Pagination.PageButton
activeClassName="bg-primary-50 text-primary-600"
className="flex items-center justify-center h-8 w-8 rounded-lg cursor-pointer"
inactiveClassName="text-gray-500"
className='flex items-center justify-center min-w-8 px-1 py-2 rounded-lg system-sm-medium cursor-pointer hover:bg-components-button-ghost-bg-hover'
activeClassName='bg-components-button-tertiary-bg text-components-button-tertiary-text hover:bg-components-button-ghost-bg-hover'
inactiveClassName='text-text-tertiary'
/>
</div>
<Pagination.NextButton
disabled={current === totalPages - 1}
className={`flex items-center mr-2 text-gray-500 focus:outline-none ${current === totalPages - 1 ? 'cursor-not-allowed opacity-50' : 'cursor-pointer hover:text-gray-600'}`} >
{t('appLog.table.pagination.next')}
<ArrowRightIcon className="ml-3 h-3 w-3" />
</Pagination.NextButton>
{onLimitChange && (
<div className='shrink-0 flex items-center gap-2'>
<div className='shrink-0 w-[51px] text-end text-text-tertiary system-2xs-regular-uppercase'>{showPerPageTip ? t('common.pagination.perPage') : ''}</div>
<div
className='flex items-center gap-[1px] p-0.5 rounded-[10px] bg-components-segmented-control-bg-normal'
onMouseEnter={() => setShowPerPageTip(true)}
onMouseLeave={() => setShowPerPageTip(false)}
>
<div
className={cn(
'px-2.5 py-1.5 rounded-lg border-[0.5px] border-transparent system-sm-medium text-text-tertiary cursor-pointer hover:bg-state-base-hover hover:text-text-secondary',
limit === 10 && 'shadow-xs border-components-segmented-control-item-active-border bg-components-segmented-control-item-active-bg text-text-secondary hover:bg-components-segmented-control-item-active-bg',
)}
onClick={() => onLimitChange?.(10)}
>10</div>
<div
className={cn(
'px-2.5 py-1.5 rounded-lg border-[0.5px] border-transparent system-sm-medium text-text-tertiary cursor-pointer hover:bg-state-base-hover hover:text-text-secondary',
limit === 25 && 'shadow-xs border-components-segmented-control-item-active-border bg-components-segmented-control-item-active-bg text-text-secondary hover:bg-components-segmented-control-item-active-bg',
)}
onClick={() => onLimitChange?.(25)}
>25</div>
<div
className={cn(
'px-2.5 py-1.5 rounded-lg border-[0.5px] border-transparent system-sm-medium text-text-tertiary cursor-pointer hover:bg-state-base-hover hover:text-text-secondary',
limit === 50 && 'shadow-xs border-components-segmented-control-item-active-border bg-components-segmented-control-item-active-bg text-text-secondary hover:bg-components-segmented-control-item-active-bg',
)}
onClick={() => onLimitChange?.(50)}
>50</div>
</div>
</div>
)}
</Pagination>
)
}

View File

@ -0,0 +1,189 @@
import React from 'react'
import clsx from 'clsx'
import usePagination from './hook'
import type {
ButtonProps,
IPagination,
IPaginationProps,
PageButtonProps,
} from './type'
const defaultState: IPagination = {
currentPage: 0,
setCurrentPage: () => {},
truncableText: '...',
truncableClassName: '',
pages: [],
hasPreviousPage: false,
hasNextPage: false,
previousPages: [],
isPreviousTruncable: false,
middlePages: [],
isNextTruncable: false,
nextPages: [],
}
const PaginationContext: React.Context<IPagination> = React.createContext<IPagination>(defaultState)
export const PrevButton = ({
className,
children,
dataTestId,
as = <button />,
...buttonProps
}: ButtonProps) => {
const pagination = React.useContext(PaginationContext)
const previous = () => {
if (pagination.currentPage + 1 > 1)
pagination.setCurrentPage(pagination.currentPage - 1)
}
const disabled = pagination.currentPage === 0
return (
<as.type
{...buttonProps}
{...as.props}
className={clsx(className, as.props.className)}
onClick={() => previous()}
tabIndex={disabled ? '-1' : 0}
disabled={disabled}
data-testid={dataTestId}
onKeyPress={(event: React.KeyboardEvent) => {
event.preventDefault()
if (event.key === 'Enter' && !disabled)
previous()
}}
>
{as.props.children ?? children}
</as.type>
)
}
export const NextButton = ({
className,
children,
dataTestId,
as = <button />,
...buttonProps
}: ButtonProps) => {
const pagination = React.useContext(PaginationContext)
const next = () => {
if (pagination.currentPage + 1 < pagination.pages.length)
pagination.setCurrentPage(pagination.currentPage + 1)
}
const disabled = pagination.currentPage === pagination.pages.length - 1
return (
<as.type
{...buttonProps}
{...as.props}
className={clsx(className, as.props.className)}
onClick={() => next()}
tabIndex={disabled ? '-1' : 0}
disabled={disabled}
data-testid={dataTestId}
onKeyPress={(event: React.KeyboardEvent) => {
event.preventDefault()
if (event.key === 'Enter' && !disabled)
next()
}}
>
{as.props.children ?? children}
</as.type>
)
}
type ITruncableElementProps = {
prev?: boolean
}
const TruncableElement = ({ prev }: ITruncableElementProps) => {
const pagination: IPagination = React.useContext(PaginationContext)
const {
isPreviousTruncable,
isNextTruncable,
truncableText,
truncableClassName,
} = pagination
return ((isPreviousTruncable && prev === true) || (isNextTruncable && !prev))
? (
<li className={truncableClassName || undefined}>{truncableText}</li>
)
: null
}
export const PageButton = ({
as = <a />,
className,
dataTestIdActive,
dataTestIdInactive,
activeClassName,
inactiveClassName,
renderExtraProps,
}: PageButtonProps) => {
const pagination: IPagination = React.useContext(PaginationContext)
const renderPageButton = (page: number) => (
<li key={page}>
<as.type
data-testid={
clsx({
[`${dataTestIdActive}`]:
dataTestIdActive && pagination.currentPage + 1 === page,
[`${dataTestIdInactive}-${page}`]:
dataTestIdActive && pagination.currentPage + 1 !== page,
}) || undefined
}
tabIndex={0}
onKeyPress={(event: React.KeyboardEvent) => {
if (event.key === 'Enter')
pagination.setCurrentPage(page - 1)
}}
onClick={() => pagination.setCurrentPage(page - 1)}
className={clsx(
className,
pagination.currentPage + 1 === page
? activeClassName
: inactiveClassName,
)}
{...as.props}
{...(renderExtraProps ? renderExtraProps(page) : {})}
>
{page}
</as.type>
</li>
)
return (
<>
{pagination.previousPages.map(renderPageButton)}
<TruncableElement prev />
{pagination.middlePages.map(renderPageButton)}
<TruncableElement />
{pagination.nextPages.map(renderPageButton)}
</>
)
}
export const Pagination = ({
dataTestId,
...paginationProps
}: IPaginationProps & { dataTestId?: string }) => {
const pagination = usePagination(paginationProps)
return (
<PaginationContext.Provider value={pagination}>
<div className={paginationProps.className} data-testid={dataTestId}>
{paginationProps.children}
</div>
</PaginationContext.Provider>
)
}
Pagination.PrevButton = PrevButton
Pagination.NextButton = NextButton
Pagination.PageButton = PageButton

View File

@ -1,3 +0,0 @@
.pagination li {
list-style: none;
}

View File

@ -0,0 +1,58 @@
import type { ButtonHTMLAttributes } from 'react'
type IBasePaginationProps = {
currentPage: number
setCurrentPage: (page: number) => void
truncableText?: string
truncableClassName?: string
}
type IPaginationProps = IBasePaginationProps & {
totalPages: number
edgePageCount: number
middlePagesSiblingCount: number
className?: string
children?: React.ReactNode
}
type IUsePagination = IBasePaginationProps & {
pages: number[]
hasPreviousPage: boolean
hasNextPage: boolean
previousPages: number[]
isPreviousTruncable: boolean
middlePages: number[]
isNextTruncable: boolean
nextPages: number[]
}
type IPagination = IUsePagination & {
setCurrentPage: (page: number) => void
}
type ButtonProps = ButtonHTMLAttributes<HTMLButtonElement> & {
as?: React.ReactElement
children?: string | React.ReactNode
className?: string
dataTestId?: string
}
type PageButtonProps = ButtonProps & {
/**
* Provide a custom ReactElement (e.g. Next/Link)
*/
as?: React.ReactElement
activeClassName?: string
inactiveClassName?: string
dataTestIdActive?: string
dataTestIdInactive?: string
renderExtraProps?: (pageNum: number) => {}
}
export type {
IPaginationProps,
IUsePagination,
IPagination,
ButtonProps,
PageButtonProps,
}

View File

@ -156,7 +156,8 @@ const StepTwo = ({
const setSegmentIdentifier = useCallback((value: string) => {
doSetSegmentIdentifier(value ? escape(value) : DEFAULT_SEGMENT_IDENTIFIER)
}, [])
const [max, setMax] = useState(4000) // default chunk length
const [maxChunkLength, setMaxChunkLength] = useState(4000) // default chunk length
const [limitMaxChunkLength, setLimitMaxChunkLength] = useState(4000)
const [overlap, setOverlap] = useState(50)
const [rules, setRules] = useState<PreProcessingRule[]>([])
const [defaultConfig, setDefaultConfig] = useState<Rules>()
@ -193,10 +194,11 @@ const StepTwo = ({
pre_processing_rules: rules,
segmentation: {
separator: unescape(segmentIdentifier),
max_tokens: max,
max_tokens: maxChunkLength,
chunk_overlap: overlap,
},
}
// @ts-expect-error will be removed after api refactored.
processRule.rules = ruleObj
}
return processRule
@ -283,7 +285,7 @@ const StepTwo = ({
const resetRules = () => {
if (defaultConfig) {
setSegmentIdentifier(defaultConfig.segmentation.separator)
setMax(defaultConfig.segmentation.max_tokens)
setMaxChunkLength(defaultConfig.segmentation.max_tokens)
setOverlap(defaultConfig.segmentation.chunk_overlap!)
setRules(defaultConfig.pre_processing_rules)
}
@ -291,7 +293,7 @@ const StepTwo = ({
}
const updatePreview = () => {
if (segmentationType === SegmentType.CUSTOM && max > 4000) {
if (segmentationType === SegmentType.CUSTOM && maxChunkLength > 4000) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
return
}
@ -318,12 +320,12 @@ const StepTwo = ({
)
const getCreationParams = () => {
let params
if (segmentationType === SegmentType.CUSTOM && overlap > max) {
if (segmentationType === SegmentType.CUSTOM && overlap > maxChunkLength) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.overlapCheck') })
return
}
if (segmentationType === SegmentType.CUSTOM && max > 4000) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck') })
if (segmentationType === SegmentType.CUSTOM && maxChunkLength > limitMaxChunkLength) {
Toast.notify({ type: 'error', message: t('datasetCreation.stepTwo.maxLengthCheck', { limit: limitMaxChunkLength }) })
return
}
if (isSetting) {
@ -398,10 +400,11 @@ const StepTwo = ({
onSuccess(data) {
const separator = data.rules.segmentation.separator
setSegmentIdentifier(separator)
setMax(data.rules.segmentation.max_tokens)
setMaxChunkLength(data.rules.segmentation.max_tokens)
setOverlap(data.rules.segmentation.chunk_overlap!)
setRules(data.rules.pre_processing_rules)
setDefaultConfig(data.rules)
setLimitMaxChunkLength(data.limits.indexing_max_segmentation_tokens_length)
},
onError(error) {
Toast.notify({
@ -418,8 +421,8 @@ const StepTwo = ({
const max = rules.segmentation.max_tokens
const overlap = rules.segmentation.chunk_overlap
setSegmentIdentifier(separator)
setMax(max)
setOverlap(overlap as number)
setMaxChunkLength(max)
setOverlap(overlap!)
setRules(rules.pre_processing_rules)
setDefaultConfig(rules)
}
@ -427,6 +430,7 @@ const StepTwo = ({
const getDefaultMode = () => {
if (documentDetail)
// @ts-expect-error fix after api refactored
setSegmentationType(documentDetail.dataset_process_rule.mode)
}
@ -516,6 +520,7 @@ const StepTwo = ({
getRulesFromDetail()
getDefaultMode()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
useEffect(() => {
@ -577,8 +582,8 @@ const StepTwo = ({
onChange={e => setSegmentIdentifier(e.target.value)}
/>
<MaxLengthInput
value={max}
onChange={setMax}
value={maxChunkLength}
onChange={setMaxChunkLength}
/>
<OverlapInput
value={overlap}

View File

@ -379,10 +379,107 @@ The text generation application offers non-session support and is ideal for tran
---
<Heading
url='/text-to-audio'
method='POST'
title='Text to Audio'
name='#audio'
/>
<Row>
<Col>
Text to speech.
### Request Body
<Properties>
<Property name='message_id' type='str' key='text'>
For text messages generated by Dify, simply pass the generated message-id directly. The backend will use the message-id to look up the corresponding content and synthesize the voice information directly. If both message_id and text are provided simultaneously, the message_id is given priority.
</Property>
<Property name='text' type='str' key='text'>
Speech generated content。
</Property>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the app.
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "Hello Dify",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='Get Application Basic Information'
name='#info'
/>
<Row>
<Col>
Used to get basic information about this application
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `name` (string) application name
- `description` (string) application description
- `tags` (array[string]) application tags
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='Get Application Information'
title='Get Application Parameters Information'
name='#parameters'
/>
<Row>
@ -497,56 +594,3 @@ The text generation application offers non-session support and is ideal for tran
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='Text to Audio'
name='#audio'
/>
<Row>
<Col>
Text to speech.
### Request Body
<Properties>
<Property name='message_id' type='str' key='text'>
For text messages generated by Dify, simply pass the generated message-id directly. The backend will use the message-id to look up the corresponding content and synthesize the voice information directly. If both message_id and text are provided simultaneously, the message_id is given priority.
</Property>
<Property name='text' type='str' key='text'>
Speech generated content。
</Property>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the app.
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "Hello Dify",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -375,13 +375,109 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='テキストから音声'
name='#audio'
/>
<Row>
<Col>
テキストを音声に変換します。
### リクエストボディ
<Properties>
<Property name='message_id' type='str' key='text'>
Difyが生成したテキストメッセージの場合、生成されたmessage-idを直接渡すだけです。バックエンドはmessage-idを使用して対応するコンテンツを検索し、音声情報を直接合成します。message_idとtextの両方が同時に提供された場合、message_idが優先されます。
</Property>
<Property name='text' type='str' key='text'>
音声生成コンテンツ。
</Property>
<Property name='user' type='string' key='user'>
開発者が定義したユーザー識別子。アプリ内で一意性を確保する必要があります。
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "Hello Dify",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='アプリケーションの基本情報を取得'
name='#info'
/>
<Row>
<Col>
このアプリケーションの基本情報を取得するために使用されます
### Query
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### Response
- `name` (string) アプリケーションの名前
- `description` (string) アプリケーションの説明
- `tags` (array[string]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='アプリケーション情報取得'
title='アプリケーションのパラメータ情報取得'
name='#parameters'
/>
<Row>
@ -496,56 +592,3 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='テキストから音声'
name='#audio'
/>
<Row>
<Col>
テキストを音声に変換します。
### リクエストボディ
<Properties>
<Property name='message_id' type='str' key='text'>
Difyが生成したテキストメッセージの場合、生成されたmessage-idを直接渡すだけです。バックエンドはmessage-idを使用して対応するコンテンツを検索し、音声情報を直接合成します。message_idとtextの両方が同時に提供された場合、message_idが優先されます。
</Property>
<Property name='text' type='str' key='text'>
音声生成コンテンツ。
</Property>
<Property name='user' type='string' key='user'>
開発者が定義したユーザー識別子。アプリ内で一意性を確保する必要があります。
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "Hello Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "Hello Dify",
"user": "abc-123"
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -353,10 +353,108 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
---
<Heading
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
/>
<Row>
<Col>
文字转语音。
### Request Body
<Properties>
<Property name='message_id' type='str' key='text'>
Dify 生成的文本消息那么直接传递生成的message-id 即可,后台会通过 message_id 查找相应的内容直接合成语音信息。如果同时传 message_id 和 text优先使用 message_id。
</Property>
<Property name='text' type='str' key='text'>
语音生成内容。如果没有传 message-id的话则会使用这个字段的内容
</Property>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "你好Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "你好Dify",
"user": "abc-123",
"streaming": false
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='获取应用基本信息'
name='#info'
/>
<Row>
<Col>
用于获取应用的基本信息
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `name` (string) 应用名称
- `description` (string) 应用描述
- `tags` (array[string]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用配置信息'
title='获取应用参数'
name='#parameters'
/>
<Row>
@ -461,57 +559,3 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/text-to-audio'
method='POST'
title='文字转语音'
name='#audio'
/>
<Row>
<Col>
文字转语音。
### Request Body
<Properties>
<Property name='message_id' type='str' key='text'>
Dify 生成的文本消息那么直接传递生成的message-id 即可,后台会通过 message_id 查找相应的内容直接合成语音信息。如果同时传 message_id 和 text优先使用 message_id。
</Property>
<Property name='text' type='str' key='text'>
语音生成内容。如果没有传 message-id的话则会使用这个字段的内容
</Property>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/text-to-audio" targetCode={`curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",\n "text": "你好Dify",\n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -o text-to-audio.mp3 -X POST '${props.appDetail.api_base_url}/text-to-audio' \
--header 'Authorization: Bearer {api_key}' \
--header 'Content-Type: application/json' \
--data-raw '{
"message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290",
"text": "你好Dify",
"user": "abc-123",
"streaming": false
}'
```
</CodeGroup>
<CodeGroup title="headers">
```json {{ title: 'headers' }}
{
"Content-Type": "audio/wav"
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -161,7 +161,7 @@ Chat applications support session persistence, allowing previous chat history to
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (array[object]) Contents of all preceding node variables used in the node
- `inputs` (object) Contents of all preceding node variables used in the node
- `created_at` (timestamp) timestamp of start, e.g., 1705395332
- `event: node_finished` node execution ends, success or failure in different states in the same event
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
@ -174,7 +174,7 @@ Chat applications support session persistence, allowing previous chat history to
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (array[object]) Contents of all preceding node variables used in the node
- `inputs` (object) Contents of all preceding node variables used in the node
- `process_data` (json) Optional node process data
- `outputs` (json) Optional content of output
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
@ -564,7 +564,7 @@ Chat applications support session persistence, allowing previous chat history to
- `data` (array[object]) Message list
- `id` (string) Message ID
- `conversation_id` (string) Conversation ID
- `inputs` (array[object]) User input parameters.
- `inputs` (object) User input parameters.
- `query` (string) User input / question content.
- `message_files` (array[object]) Message files
- `id` (string) ID
@ -648,16 +648,13 @@ Chat applications support session persistence, allowing previous chat history to
Should be uniquely defined by the developer within the application.
</Property>
<Property name='last_id' type='string' key='last_id'>
The ID of the last record on the current page, default is null.
(Optional) The ID of the last record on the current page, default is null.
</Property>
<Property name='limit' type='int' key='limit'>
How many records to return in one request, default is the most recent 20 entries.
</Property>
<Property name='pinned' type='bool' key='pinned'>
Return only pinned conversations as `true`, only non-pinned as `false`
(Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1.
</Property>
<Property name='sort_by' type='string' key='sort_by'>
Sorting Field (Optional), Default: -updated_at (sorted in descending order by update time)
(Optional) Sorting Field, Default: -updated_at (sorted in descending order by update time)
- Available Values: created_at, -created_at, updated_at, -updated_at
- The symbol before the field represents the order or reverse, "-" represents reverse order.
</Property>
@ -667,9 +664,11 @@ Chat applications support session persistence, allowing previous chat history to
- `data` (array[object]) List of conversations
- `id` (string) Conversation ID
- `name` (string) Conversation name, by default, is generated by LLM.
- `inputs` (array[object]) User input parameters.
- `inputs` (object) User input parameters.
- `status` (string) Conversation status
- `introduction` (string) Introduction
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
- `updated_at` (timestamp) Update timestamp, e.g., 1705395332
- `has_more` (bool)
- `limit` (int) Number of entries returned, if input exceeds system limit, system limit number is returned
@ -699,7 +698,8 @@ Chat applications support session persistence, allowing previous chat history to
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -781,10 +781,10 @@ Chat applications support session persistence, allowing previous chat history to
<Properties>
<Property name='name' type='string' key='name'>
The name of the conversation. This parameter can be omitted if `auto_generate` is set to `true`.
(Optional) The name of the conversation. This parameter can be omitted if `auto_generate` is set to `true`.
</Property>
<Property name='auto_generate' type='bool' key='auto_generate'>
Automatically generate the title, default is `false`
(Optional) Automatically generate the title, default is `false`
</Property>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the application.
@ -794,13 +794,15 @@ Chat applications support session persistence, allowing previous chat history to
### Response
- `id` (string) Conversation ID
- `name` (string) Conversation name
- `inputs` array[object] User input parameters.
- `inputs` (object) User input parameters
- `status` (string) Conversation status
- `introduction` (string) Introduction
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
- `updated_at` (timestamp) Update timestamp, e.g., 1705395332
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -808,6 +810,7 @@ Chat applications support session persistence, allowing previous chat history to
--header 'Authorization: Bearer {api_key}' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -820,8 +823,10 @@ Chat applications support session persistence, allowing previous chat history to
"id": "cd78daf6-f9e4-4463-9ff2-54257230a0ce",
"name": "Chat vs AI",
"inputs": {},
"status": "normal",
"introduction": "",
"created_at": 1705569238
"created_at": 1705569238,
"updated_at": 1705569238
}
```
</CodeGroup>
@ -931,13 +936,57 @@ Chat applications support session persistence, allowing previous chat history to
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='Get Application Basic Information'
name='#info'
/>
<Row>
<Col>
Used to get basic information about this application
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `name` (string) application name
- `description` (string) application description
- `tags` (array[string]) application tags
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='Get Application Information'
title='Get Application Parameters Information'
name='#parameters'
/>
<Row>
@ -1091,14 +1140,14 @@ Chat applications support session persistence, allowing previous chat history to
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"tool_icons": {
"tool_icons": {
"dalle2": "https://cloud.dify.ai/console/api/workspaces/current/tool-provider/builtin/dalle/icon",
"api_tool": {
"background": "#252525",
"content": "\ud83d\ude01"
"background": "#252525",
"content": "\ud83d\ude01"
}
}
}
}
```
</CodeGroup>
</Col>

View File

@ -161,7 +161,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `created_at` (timestamp) 開始のタイムスタンプ、例1705395332
- `event: node_finished` ノード実行が終了、成功または失敗は同じイベント内で異なる状態で示されます
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
@ -174,7 +174,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `process_data` (json) オプションのノードプロセスデータ
- `outputs` (json) オプションの出力内容
- `status` (string) 実行の状態、`running` / `succeeded` / `failed` / `stopped`
@ -564,7 +564,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `data` (array[object]) メッセージリスト
- `id` (string) メッセージID
- `conversation_id` (string) 会話ID
- `inputs` (array[object]) ユーザー入力パラメータ。
- `inputs` (object) ユーザー入力パラメータ。
- `query` (string) ユーザー入力/質問内容。
- `message_files` (array[object]) メッセージファイル
- `id` (string) ID
@ -648,16 +648,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
アプリケーション内で開発者によって一意に定義されるべきです。
</Property>
<Property name='last_id' type='string' key='last_id'>
現在のページの最後の記録のID、デフォルトはnullです。
(Optional)現在のページの最後の記録のID、デフォルトはnullです。
</Property>
<Property name='limit' type='int' key='limit'>
1回のリクエストで返す記録の数、デフォルトは最新の20件です。
</Property>
<Property name='pinned' type='bool' key='pinned'>
ピン留めされた会話のみを`true`として返し、非ピン留めを`false`として返します
(Optional)1回のリクエストで返す記録の数、デフォルトは最新の20件です。最大100、最小1。
</Property>
<Property name='sort_by' type='string' key='sort_by'>
ソートフィールド(オプション)、デフォルト:-updated_at更新時間で降順にソート
(Optional)ソートフィールド、デフォルト:-updated_at更新時間で降順にソート
- 利用可能な値created_at, -created_at, updated_at, -updated_at
- フィールドの前の記号は順序または逆順を表し、"-"は逆順を表します。
</Property>
@ -667,9 +664,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `data` (array[object]) 会話のリスト
- `id` (string) 会話ID
- `name` (string) 会話名、デフォルトではLLMによって生成されます。
- `inputs` (array[object]) ユーザー入力パラメータ。
- `inputs` (object) ユーザー入力パラメータ。
- `introduction` (string) 紹介
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
- `updated_at` (timestamp) 更新タイムスタンプ、例1705395332
- `has_more` (bool)
- `limit` (int) 返されたエントリ数、入力がシステム制限を超える場合、システム制限数が返されます
@ -699,7 +697,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -781,10 +780,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<Properties>
<Property name='name' type='string' key='name'>
会話の名前。`auto_generate`が`true`に設定されている場合、このパラメータは省略できます。
(Optional)会話の名前。`auto_generate`が`true`に設定されている場合、このパラメータは省略できます。
</Property>
<Property name='auto_generate' type='bool' key='auto_generate'>
タイトルを自動生成、デフォルトは`false`
(Optional)タイトルを自動生成、デフォルトは`false`
</Property>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者によって定義され、アプリケーション内で一意であることを保証しなければなりません。
@ -794,13 +793,15 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
### 応答
- `id` (string) 会話ID
- `name` (string) 会話名
- `inputs` array[object] ユーザー入力パラメータ
- `inputs` (object) ユーザー入力パラメータ
- `status` (string) 会話状態
- `introduction` (string) 紹介
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
- `updated_at` (timestamp) 更新タイムスタンプ、例1705395332
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="リクエスト" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -808,6 +809,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
--header 'Authorization: Bearer {api_key}' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -820,8 +822,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"id": "cd78daf6-f9e4-4463-9ff2-54257230a0ce",
"name": "チャット vs AI",
"inputs": {},
"status": "normal",
"introduction": "",
"created_at": 1705569238
"created_at": 1705569238,
"updated_at": 1705569238
}
```
</CodeGroup>
@ -931,13 +935,57 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='アプリケーションの基本情報を取得'
name='#info'
/>
<Row>
<Col>
このアプリケーションの基本情報を取得するために使用されます
### Query
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### Response
- `name` (string) アプリケーションの名前
- `description` (string) アプリケーションの説明
- `tags` (array[string]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='アプリケーション情報を取得'
title='アプリケーションのパラメータ情報を取得'
name='#parameters'
/>
<Row>
@ -1057,7 +1105,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<Heading
url='/meta'
method='GET'
title='アプリケーションメタ情報を取得'
title='アプリケーションメタ情報を取得'
name='#meta'
/>
<Row>
@ -1091,14 +1139,14 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"tool_icons": {
"tool_icons": {
"dalle2": "https://cloud.dify.ai/console/api/workspaces/current/tool-provider/builtin/dalle/icon",
"api_tool": {
"background": "#252525",
"content": "\ud83d\ude01"
"background": "#252525",
"content": "\ud83d\ude01"
}
}
}
}
```
</CodeGroup>
</Col>

View File

@ -162,7 +162,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `title` (string) 节点名称
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) 前置节点 ID用于画布展示执行路径
- `inputs` (array[object]) 节点中所有使用到的前置节点变量内容
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `created_at` (timestamp) 开始时间
- `event: node_finished` node 执行结束,成功失败同一事件中不同状态
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
@ -173,7 +173,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `node_id` (string) 节点 ID
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) optional 前置节点 ID用于画布展示执行路径
- `inputs` (array[object]) 节点中所有使用到的前置节点变量内容
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `process_data` (json) Optional 节点过程数据
- `outputs` (json) Optional 输出内容
- `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped`
@ -570,7 +570,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `data` (array[object]) 消息列表
- `id` (string) 消息 ID
- `conversation_id` (string) 会话 ID
- `inputs` (array[object]) 用户输入参数。
- `inputs` (object) 用户输入参数。
- `query` (string) 用户输入 / 提问内容。
- `message_files` (array[object]) 消息文件
- `id` (string) ID
@ -683,16 +683,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
<Property name='last_id' type='string' key='last_id'>
当前页最后面一条记录的 ID默认 null
(选填)当前页最后面一条记录的 ID默认 null
</Property>
<Property name='limit' type='int' key='limit'>
一次请求返回多少条记录
</Property>
<Property name='pinned' type='bool' key='pinned'>
只返回置顶 true只返回非置顶 false
(选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。
</Property>
<Property name='sort_by' type='string' key='sort_by'>
排序字段(选题),默认 -updated_at(按更新时间倒序排列)
(选填)排序字段,默认 -updated_at(按更新时间倒序排列)
- 可选值created_at, -created_at, updated_at, -updated_at
- 字段前面的符号代表顺序或倒序,-代表倒序
</Property>
@ -702,9 +699,11 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `data` (array[object]) 会话列表
- `id` (string) 会话 ID
- `name` (string) 会话名称,默认由大语言模型生成。
- `inputs` (array[object]) 用户输入参数。
- `inputs` (object) 用户输入参数。
- `status` (string) 会话状态
- `introduction` (string) 开场白
- `created_at` (timestamp) 创建时间
- `updated_at` (timestamp) 更新时间
- `has_more` (bool)
- `limit` (int) 返回条数,若传入超过系统限制,返回系统限制数量
@ -734,7 +733,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -817,10 +817,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<Properties>
<Property name='name' type='string' key='name'>
名称,若 `auto_generate` 为 `true` 时,该参数可不传。
(选填)名称,若 `auto_generate` 为 `true` 时,该参数可不传。
</Property>
<Property name='auto_generate' type='string' key='auto_generate'>
自动生成标题,默认 false。
<Property name='auto_generate' type='bool' key='auto_generate'>
(选填)自动生成标题,默认 false。
</Property>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
@ -830,13 +830,15 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
### Response
- `id` (string) 会话 ID
- `name` (string) 会话名称
- `inputs` array[object] 用户输入参数
- `inputs` (object) 用户输入参数
- `status` (string) 会话状态
- `introduction` (string) 开场白
- `created_at` (timestamp) 创建时间
- `updated_at` (timestamp) 更新时间
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -844,6 +846,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
--header 'Content-Type: application/json' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -853,7 +856,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
"id": "34d511d5-56de-4f16-a997-57b379508443",
"name": "hello",
"inputs": {},
"status": "normal",
"introduction": "",
"created_at": 1732731141,
"updated_at": 1732734510
}
```
</CodeGroup>
@ -960,13 +969,57 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='获取应用基本信息'
name='#info'
/>
<Row>
<Col>
用于获取应用的基本信息
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `name` (string) 应用名称
- `description` (string) 应用描述
- `tags` (array[string]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用配置信息'
title='获取应用参数'
name='#parameters'
/>
<Row>

View File

@ -528,7 +528,7 @@ Chat applications support session persistence, allowing previous chat history to
- `data` (array[object]) Message list
- `id` (string) Message ID
- `conversation_id` (string) Conversation ID
- `inputs` (array[object]) User input parameters.
- `inputs` (object) User input parameters.
- `query` (string) User input / question content.
- `message_files` (array[object]) Message files
- `id` (string) ID
@ -682,16 +682,13 @@ Chat applications support session persistence, allowing previous chat history to
Should be uniquely defined by the developer within the application.
</Property>
<Property name='last_id' type='string' key='last_id'>
The ID of the last record on the current page, default is null.
(Optional) The ID of the last record on the current page, default is null.
</Property>
<Property name='limit' type='int' key='limit'>
How many records to return in one request, default is the most recent 20 entries.
</Property>
<Property name='pinned' type='bool' key='pinned'>
Return only pinned conversations as `true`, only non-pinned as `false`
(Optional) How many records to return in one request, default is the most recent 20 entries. Maximum 100, minimum 1.
</Property>
<Property name='sort_by' type='string' key='sort_by'>
Sorting Field (Optional), Default: -updated_at (sorted in descending order by update time)
(Optional) Sorting Field, Default: -updated_at (sorted in descending order by update time)
- Available Values: created_at, -created_at, updated_at, -updated_at
- The symbol before the field represents the order or reverse, "-" represents reverse order.
</Property>
@ -701,9 +698,11 @@ Chat applications support session persistence, allowing previous chat history to
- `data` (array[object]) List of conversations
- `id` (string) Conversation ID
- `name` (string) Conversation name, by default, is a snippet of the first question asked by the user in the conversation.
- `inputs` (array[object]) User input parameters.
- `inputs` (object) User input parameters.
- `status` (string) Conversation status
- `introduction` (string) Introduction
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
- `updated_at` (timestamp) Update timestamp, e.g., 1705395332
- `has_more` (bool)
- `limit` (int) Number of entries returned, if input exceeds system limit, system limit number is returned
@ -733,7 +732,8 @@ Chat applications support session persistence, allowing previous chat history to
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -815,10 +815,10 @@ Chat applications support session persistence, allowing previous chat history to
<Properties>
<Property name='name' type='string' key='name'>
The name of the conversation. This parameter can be omitted if `auto_generate` is set to `true`.
(Optional) The name of the conversation. This parameter can be omitted if `auto_generate` is set to `true`.
</Property>
<Property name='auto_generate' type='bool' key='auto_generate'>
Automatically generate the title, default is `false`
(Optional) Automatically generate the title, default is `false`
</Property>
<Property name='user' type='string' key='user'>
The user identifier, defined by the developer, must ensure uniqueness within the application.
@ -828,13 +828,15 @@ Chat applications support session persistence, allowing previous chat history to
### Response
- `id` (string) Conversation ID
- `name` (string) Conversation name
- `inputs` array[object] User input parameters.
- `inputs` (object) User input parameters
- `status` (string) Conversation status
- `introduction` (string) Introduction
- `created_at` (timestamp) Creation timestamp, e.g., 1705395332
- `updated_at` (timestamp) Update timestamp, e.g., 1705395332
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -842,6 +844,7 @@ Chat applications support session persistence, allowing previous chat history to
--header 'Authorization: Bearer {api_key}' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -854,8 +857,10 @@ Chat applications support session persistence, allowing previous chat history to
"id": "cd78daf6-f9e4-4463-9ff2-54257230a0ce",
"name": "Chat vs AI",
"inputs": {},
"status": "normal",
"introduction": "",
"created_at": 1705569238
"created_at": 1705569238,
"updated_at": 1705569238
}
```
</CodeGroup>
@ -960,13 +965,57 @@ Chat applications support session persistence, allowing previous chat history to
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='Get Application Basic Information'
name='#info'
/>
<Row>
<Col>
Used to get basic information about this application
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `name` (string) application name
- `description` (string) application description
- `tags` (array[string]) application tags
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='Get Application Information'
title='Get Application Parameters Information'
name='#parameters'
/>
<Row>
@ -1120,14 +1169,14 @@ Chat applications support session persistence, allowing previous chat history to
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"tool_icons": {
"tool_icons": {
"dalle2": "https://cloud.dify.ai/console/api/workspaces/current/tool-provider/builtin/dalle/icon",
"api_tool": {
"background": "#252525",
"content": "\ud83d\ude01"
"background": "#252525",
"content": "\ud83d\ude01"
}
}
}
}
```
</CodeGroup>
</Col>

View File

@ -528,7 +528,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `data` (array[object]) メッセージリスト
- `id` (string) メッセージID
- `conversation_id` (string) 会話ID
- `inputs` (array[object]) ユーザー入力パラメータ。
- `inputs` (object) ユーザー入力パラメータ。
- `query` (string) ユーザー入力/質問内容。
- `message_files` (array[object]) メッセージファイル
- `id` (string) ID
@ -682,16 +682,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
アプリケーション内で開発者によって一意に定義される必要があります。
</Property>
<Property name='last_id' type='string' key='last_id'>
現在のページの最後のレコードのID、デフォルトはnullです。
(Optional)現在のページの最後のレコードのID、デフォルトはnullです。
</Property>
<Property name='limit' type='int' key='limit'>
1回のリクエストで返すレコードの数、デフォルトは最新の20件です。
</Property>
<Property name='pinned' type='bool' key='pinned'>
ピン留めされた会話のみを`true`として返し、ピン留めされていないもののみを`false`として返します
(Optional)1回のリクエストで返すレコードの数、デフォルトは最新の20件です。最大100、最小1。
</Property>
<Property name='sort_by' type='string' key='sort_by'>
ソートフィールド(オプション)、デフォルト:-updated_at更新時間で降順にソート
(Optional)ソートフィールド、デフォルト:-updated_at更新時間で降順にソート
- 利用可能な値created_at, -created_at, updated_at, -updated_at
- フィールドの前の記号は順序または逆順を表し、"-"は逆順を表します。
</Property>
@ -701,9 +698,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `data` (array[object]) 会話のリスト
- `id` (string) 会話ID
- `name` (string) 会話名、デフォルトでは、ユーザーが会話で最初に尋ねた質問のスニペットです。
- `inputs` (array[object]) ユーザー入力パラメータ。
- `inputs` (object) ユーザー入力パラメータ。
- `introduction` (string) 紹介
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
- `updated_at` (timestamp) 更新タイムスタンプ、例1705395332
- `has_more` (bool)
- `limit` (int) 返されたエントリの数、入力がシステム制限を超える場合、システム制限の数を返します
@ -733,7 +731,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -815,10 +814,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<Properties>
<Property name='name' type='string' key='name'>
会話の名前。このパラメータは、`auto_generate`が`true`に設定されている場合、省略できます。
(Optional)会話の名前。このパラメータは、`auto_generate`が`true`に設定されている場合、省略できます。
</Property>
<Property name='auto_generate' type='bool' key='auto_generate'>
タイトルを自動生成します。デフォルトは`false`です。
(Optional)タイトルを自動生成します。デフォルトは`false`です。
</Property>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者によって定義され、アプリケーション内で一意である必要があります。
@ -828,13 +827,15 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
### 応答
- `id` (string) 会話ID
- `name` (string) 会話名
- `inputs` array[object] ユーザー入力パラメータ
- `inputs` (object) ユーザー入力パラメータ
- `status` (string) 会話状態
- `introduction` (string) 紹介
- `created_at` (timestamp) 作成タイムスタンプ、例1705395332
- `updated_at` (timestamp) 更新タイムスタンプ、例1705395332
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="リクエスト" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -842,6 +843,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
--header 'Authorization: Bearer {api_key}' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -855,7 +857,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
"name": "Chat vs AI",
"inputs": {},
"introduction": "",
"created_at": 1705569238
"created_at": 1705569238,
"updated_at": 1705569238
}
```
</CodeGroup>
@ -960,13 +963,57 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='アプリケーションの基本情報を取得'
name='#info'
/>
<Row>
<Col>
このアプリケーションの基本情報を取得するために使用されます
### Query
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### Response
- `name` (string) アプリケーションの名前
- `description` (string) アプリケーションの説明
- `tags` (array[string]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='アプリケーション情報を取得'
title='アプリケーションのパラメータ情報を取得'
name='#parameters'
/>
<Row>
@ -1086,7 +1133,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<Heading
url='/meta'
method='GET'
title='アプリケーションメタ情報を取得'
title='アプリケーションメタ情報を取得'
name='#meta'
/>
<Row>
@ -1120,14 +1167,14 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"tool_icons": {
"tool_icons": {
"dalle2": "https://cloud.dify.ai/console/api/workspaces/current/tool-provider/builtin/dalle/icon",
"api_tool": {
"background": "#252525",
"content": "\ud83d\ude01"
"background": "#252525",
"content": "\ud83d\ude01"
}
}
}
}
```
</CodeGroup>
</Col>

View File

@ -543,7 +543,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `data` (array[object]) 消息列表
- `id` (string) 消息 ID
- `conversation_id` (string) 会话 ID
- `inputs` (array[object]) 用户输入参数。
- `inputs` (object) 用户输入参数。
- `query` (string) 用户输入 / 提问内容。
- `message_files` (array[object]) 消息文件
- `id` (string) ID
@ -697,16 +697,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
<Property name='last_id' type='string' key='last_id'>
当前页最后面一条记录的 ID默认 null
(选填)当前页最后面一条记录的 ID默认 null
</Property>
<Property name='limit' type='int' key='limit'>
一次请求返回多少条记录
</Property>
<Property name='pinned' type='bool' key='pinned'>
只返回置顶 true只返回非置顶 false
(选填)一次请求返回多少条记录,默认 20 条,最大 100 条,最小 1 条。
</Property>
<Property name='sort_by' type='string' key='sort_by'>
排序字段(选题),默认 -updated_at(按更新时间倒序排列)
(选填)排序字段,默认 -updated_at(按更新时间倒序排列)
- 可选值created_at, -created_at, updated_at, -updated_at
- 字段前面的符号代表顺序或倒序,-代表倒序
</Property>
@ -716,9 +713,11 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
- `data` (array[object]) 会话列表
- `id` (string) 会话 ID
- `name` (string) 会话名称,默认为会话中用户最开始问题的截取。
- `inputs` (array[object]) 用户输入参数。
- `inputs` (object) 用户输入参数。
- `status` (string) 会话状态
- `introduction` (string) 开场白
- `created_at` (timestamp) 创建时间
- `updated_at` (timestamp) 更新时间
- `has_more` (bool)
- `limit` (int) 返回条数,若传入超过系统限制,返回系统限制数量
@ -748,7 +747,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
"myName": "Lucy"
},
"status": "normal",
"created_at": 1679667915
"created_at": 1679667915,
"updated_at": 1679667915
},
{
"id": "hSIhXBhNe8X1d8Et"
@ -831,10 +831,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<Properties>
<Property name='name' type='string' key='name'>
名称,若 `auto_generate` 为 `true` 时,该参数可不传。
(选填)名称,若 `auto_generate` 为 `true` 时,该参数可不传。
</Property>
<Property name='auto_generate' type='string' key='auto_generate'>
自动生成标题,默认 false。
<Property name='auto_generate' type='bool' key='auto_generate'>
(选填)自动生成标题,默认 false。
</Property>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
@ -844,13 +844,15 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
### Response
- `id` (string) 会话 ID
- `name` (string) 会话名称
- `inputs` array[object] 用户输入参数
- `inputs` (object) 用户输入参数
- `status` (string) 会话状态
- `introduction` (string) 开场白
- `created_at` (timestamp) 创建时间
- `updated_at` (timestamp) 更新时间
</Col>
<Col sticky>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "user": "abc-123"\n}'`}>
<CodeGroup title="Request" tag="POST" label="/conversations/:conversation_id/name" targetCode={`curl -X POST '${props.appDetail.api_base_url}/conversations/:conversation_id/name' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{ \n "name": "", \n "auto_generate": true, \n "user": "abc-123"\n}'`}>
```bash {{ title: 'cURL' }}
curl -X POST '${props.appDetail.api_base_url}/conversations/{conversation_id}/name' \
@ -858,6 +860,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
--header 'Content-Type: application/json' \
--data-raw '{
"name": "",
"auto_generate": true,
"user": "abc-123"
}'
```
@ -867,7 +870,13 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"result": "success"
"id": "34d511d5-56de-4f16-a997-57b379508443",
"name": "hello",
"inputs": {},
"status": "normal",
"introduction": "",
"created_at": 1732731141,
"updated_at": 1732734510
}
```
</CodeGroup>
@ -969,13 +978,57 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='获取应用基本信息'
name='#info'
/>
<Row>
<Col>
用于获取应用的基本信息
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `name` (string) 应用名称
- `description` (string) 应用描述
- `tags` (array[string]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用配置信息'
title='获取应用参数'
name='#parameters'
/>
<Row>

View File

@ -113,7 +113,7 @@ Workflow applications offers non-session support and is ideal for translation, a
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (array[object]) Contents of all preceding node variables used in the node
- `inputs` (object) Contents of all preceding node variables used in the node
- `created_at` (timestamp) timestamp of start, e.g., 1705395332
- `event: node_finished` node execution ends, success or failure in different states in the same event
- `task_id` (string) Task ID, used for request tracking and the below Stop Generate API
@ -126,7 +126,7 @@ Workflow applications offers non-session support and is ideal for translation, a
- `title` (string) name of node
- `index` (int) Execution sequence number, used to display Tracing Node sequence
- `predecessor_node_id` (string) optional Prefix node ID, used for canvas display execution path
- `inputs` (array[object]) Contents of all preceding node variables used in the node
- `inputs` (object) Contents of all preceding node variables used in the node
- `process_data` (json) Optional node process data
- `outputs` (json) Optional content of output
- `status` (string) status of execution, `running` / `succeeded` / `failed` / `stopped`
@ -498,104 +498,6 @@ Workflow applications offers non-session support and is ideal for translation, a
---
<Heading
url='/parameters'
method='GET'
title='Get Application Information'
name='#parameters'
/>
<Row>
<Col>
Used at the start of entering the page to obtain information such as features, input parameter names, types, and default values.
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `user_input_form` (array[object]) User input form configuration
- `text-input` (object) Text input control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `paragraph` (object) Paragraph text input control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `select` (object) Dropdown control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `options` (array[string]) Option values
- `file_upload` (object) File upload configuration
- `image` (object) Image settings
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) Whether it is enabled
- `number_limits` (int) Image number limit, default is 3
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
- `system_parameters` (object) System parameters
- `file_size_limit` (int) Document upload size limit (MB)
- `image_file_size_limit` (int) Image file upload size limit (MB)
- `audio_file_size_limit` (int) Audio file upload size limit (MB)
- `video_file_size_limit` (int) Video file upload size limit (MB)
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'
@ -699,3 +601,145 @@ Workflow applications offers non-session support and is ideal for translation, a
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='Get Application Basic Information'
name='#info'
/>
<Row>
<Col>
Used to get basic information about this application
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `name` (string) application name
- `description` (string) application description
- `tags` (array[string]) application tags
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='Get Application Parameters Information'
name='#parameters'
/>
<Row>
<Col>
Used at the start of entering the page to obtain information such as features, input parameter names, types, and default values.
### Query
<Properties>
<Property name='user' type='string' key='user'>
User identifier, defined by the developer's rules, must be unique within the application.
</Property>
</Properties>
### Response
- `user_input_form` (array[object]) User input form configuration
- `text-input` (object) Text input control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `paragraph` (object) Paragraph text input control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `select` (object) Dropdown control
- `label` (string) Variable display label name
- `variable` (string) Variable ID
- `required` (bool) Whether it is required
- `default` (string) Default value
- `options` (array[string]) Option values
- `file_upload` (object) File upload configuration
- `image` (object) Image settings
Currently only supports image types: `png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) Whether it is enabled
- `number_limits` (int) Image number limit, default is 3
- `transfer_methods` (array[string]) List of transfer methods, remote_url, local_file, must choose one
- `system_parameters` (object) System parameters
- `file_size_limit` (int) Document upload size limit (MB)
- `image_file_size_limit` (int) Image file upload size limit (MB)
- `audio_file_size_limit` (int) Audio file upload size limit (MB)
- `video_file_size_limit` (int) Video file upload size limit (MB)
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -113,7 +113,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `created_at` (timestamp) 開始のタイムスタンプ、例1705395332
- `event: node_finished` ノード実行終了、同じイベントで異なる状態で成功または失敗
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
@ -126,7 +126,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
- `title` (string) ノードの名前
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
- `predecessor_node_id` (string) オプションのプレフィックスードID、キャンバス表示実行パスに使用
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
- `inputs` (object) ノードで使用されるすべての前のノード変数の内容
- `process_data` (json) オプションのノードプロセスデータ
- `outputs` (json) オプションの出力内容
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
@ -498,104 +498,6 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
---
<Heading
url='/parameters'
method='GET'
title='アプリケーション情報を取得'
name='#parameters'
/>
<Row>
<Col>
ページに入る際に、機能、入力パラメータ名、タイプ、デフォルト値などの情報を取得するために使用されます。
### クエリ
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### 応答
- `user_input_form` (array[object]) ユーザー入力フォームの設定
- `text-input` (object) テキスト入力コントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `paragraph` (object) 段落テキスト入力コントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `select` (object) ドロップダウンコントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `options` (array[string]) オプション値
- `file_upload` (object) ファイルアップロード設定
- `image` (object) 画像設定
現在サポートされている画像タイプのみ:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) 有効かどうか
- `number_limits` (int) 画像数の制限、デフォルトは3
- `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります
- `system_parameters` (object) システムパラメータ
- `file_size_limit` (int) ドキュメントアップロードサイズ制限MB
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限MB
- `audio_file_size_limit` (int) オーディオファイルアップロードサイズ制限MB
- `video_file_size_limit` (int) ビデオファイルアップロードサイズ制限MB
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'
@ -699,3 +601,145 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='アプリケーションの基本情報を取得'
name='#info'
/>
<Row>
<Col>
このアプリケーションの基本情報を取得するために使用されます
### Query
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールによって定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### Response
- `name` (string) アプリケーションの名前
- `description` (string) アプリケーションの説明
- `tags` (array[string]) アプリケーションのタグ
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='アプリケーションのパラメータ情報を取得'
name='#parameters'
/>
<Row>
<Col>
ページに入る際に、機能、入力パラメータ名、タイプ、デフォルト値などの情報を取得するために使用されます。
### クエリ
<Properties>
<Property name='user' type='string' key='user'>
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
</Property>
</Properties>
### 応答
- `user_input_form` (array[object]) ユーザー入力フォームの設定
- `text-input` (object) テキスト入力コントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `paragraph` (object) 段落テキスト入力コントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `select` (object) ドロップダウンコントロール
- `label` (string) 変数表示ラベル名
- `variable` (string) 変数ID
- `required` (bool) 必須かどうか
- `default` (string) デフォルト値
- `options` (array[string]) オプション値
- `file_upload` (object) ファイルアップロード設定
- `image` (object) 画像設定
現在サポートされている画像タイプのみ:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) 有効かどうか
- `number_limits` (int) 画像数の制限、デフォルトは3
- `transfer_methods` (array[string]) 転送方法のリスト、remote_url, local_file、いずれかを選択する必要があります
- `system_parameters` (object) システムパラメータ
- `file_size_limit` (int) ドキュメントアップロードサイズ制限MB
- `image_file_size_limit` (int) 画像ファイルアップロードサイズ制限MB
- `audio_file_size_limit` (int) オーディオファイルアップロードサイズ制限MB
- `video_file_size_limit` (int) ビデオファイルアップロードサイズ制限MB
</Col>
<Col sticky>
<CodeGroup title="リクエスト" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="応答">
```json {{ title: '応答' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -111,7 +111,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
- `title` (string) 节点名称
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) 前置节点 ID用于画布展示执行路径
- `inputs` (array[object]) 节点中所有使用到的前置节点变量内容
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `created_at` (timestamp) 开始时间
- `event: node_finished` node 执行结束,成功失败同一事件中不同状态
- `task_id` (string) 任务 ID用于请求跟踪和下方的停止响应接口
@ -122,7 +122,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
- `node_id` (string) 节点 ID
- `index` (int) 执行序号,用于展示 Tracing Node 顺序
- `predecessor_node_id` (string) optional 前置节点 ID用于画布展示执行路径
- `inputs` (array[object]) 节点中所有使用到的前置节点变量内容
- `inputs` (object) 节点中所有使用到的前置节点变量内容
- `process_data` (json) Optional 节点过程数据
- `outputs` (json) Optional 输出内容
- `status` (string) 执行状态 `running` / `succeeded` / `failed` / `stopped`
@ -490,104 +490,6 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用配置信息'
name='#parameters'
/>
<Row>
<Col>
用于进入页面一开始,获取功能开关、输入参数名称、类型及默认值等使用。
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `user_input_form` (array[object]) 用户输入表单配置
- `text-input` (object) 文本输入控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `paragraph` (object) 段落文本输入控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `select` (object) 下拉控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `options` (array[string]) 选项值
- `file_upload` (object) 文件上传配置
- `image` (object) 图片设置
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) 是否开启
- `number_limits` (int) 图片数量限制,默认 3
- `transfer_methods` (array[string]) 传递方式列表remote_url , local_file必选一个
- `system_parameters` (object) 系统参数
- `file_size_limit` (int) 文档上传大小限制 (MB)
- `image_file_size_limit` (int) 图片文件上传大小限制MB
- `audio_file_size_limit` (int) 音频文件上传大小限制 (MB)
- `video_file_size_limit` (int) 视频文件上传大小限制 (MB)
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/workflows/logs'
method='GET'
@ -691,3 +593,145 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/info'
method='GET'
title='获取应用基本信息'
name='#info'
/>
<Row>
<Col>
用于获取应用的基本信息
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `name` (string) 应用名称
- `description` (string) 应用描述
- `tags` (array[string]) 应用标签
</Col>
<Col>
<CodeGroup title="Request" tag="GET" label="/info" targetCode={`curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \\\n-H 'Authorization: Bearer {api_key}'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/info?user=abc-123' \
-H 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"name": "My App",
"description": "This is my app.",
"tags": [
"tag1",
"tag2"
]
}
```
</CodeGroup>
</Col>
</Row>
---
<Heading
url='/parameters'
method='GET'
title='获取应用参数'
name='#parameters'
/>
<Row>
<Col>
用于进入页面一开始,获取功能开关、输入参数名称、类型及默认值等使用。
### Query
<Properties>
<Property name='user' type='string' key='user'>
用户标识,由开发者定义规则,需保证用户标识在应用内唯一。
</Property>
</Properties>
### Response
- `user_input_form` (array[object]) 用户输入表单配置
- `text-input` (object) 文本输入控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `paragraph` (object) 段落文本输入控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `select` (object) 下拉控件
- `label` (string) 控件展示标签名
- `variable` (string) 控件 ID
- `required` (bool) 是否必填
- `default` (string) 默认值
- `options` (array[string]) 选项值
- `file_upload` (object) 文件上传配置
- `image` (object) 图片设置
当前仅支持图片类型:`png`, `jpg`, `jpeg`, `webp`, `gif`
- `enabled` (bool) 是否开启
- `number_limits` (int) 图片数量限制,默认 3
- `transfer_methods` (array[string]) 传递方式列表remote_url , local_file必选一个
- `system_parameters` (object) 系统参数
- `file_size_limit` (int) 文档上传大小限制 (MB)
- `image_file_size_limit` (int) 图片文件上传大小限制MB
- `audio_file_size_limit` (int) 音频文件上传大小限制 (MB)
- `video_file_size_limit` (int) 视频文件上传大小限制 (MB)
</Col>
<Col sticky>
<CodeGroup title="Request" tag="GET" label="/parameters" targetCode={` curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123'`}>
```bash {{ title: 'cURL' }}
curl -X GET '${props.appDetail.api_base_url}/parameters?user=abc-123' \
--header 'Authorization: Bearer {api_key}'
```
</CodeGroup>
<CodeGroup title="Response">
```json {{ title: 'Response' }}
{
"user_input_form": [
{
"paragraph": {
"label": "Query",
"variable": "query",
"required": true,
"default": ""
}
}
],
"file_upload": {
"image": {
"enabled": false,
"number_limits": 3,
"detail": "high",
"transfer_methods": [
"remote_url",
"local_file"
]
}
},
"system_parameters": {
"file_size_limit": 15,
"image_file_size_limit": 10,
"audio_file_size_limit": 50,
"video_file_size_limit": 100
}
}
```
</CodeGroup>
</Col>
</Row>

View File

@ -25,22 +25,18 @@ const Popup: FC<PopupProps> = ({
const language = useLanguage()
const [searchText, setSearchText] = useState('')
const filteredModelList = modelList.filter(
model => model.models.filter(
(modelItem) => {
if (modelItem.label[language] !== undefined)
return modelItem.label[language].toLowerCase().includes(searchText.toLowerCase())
const filteredModelList = modelList.map((model) => {
const filteredModels = model.models.filter((modelItem) => {
if (modelItem.label[language] !== undefined)
return modelItem.label[language].toLowerCase().includes(searchText.toLowerCase())
let found = false
Object.keys(modelItem.label).forEach((key) => {
if (modelItem.label[key].toLowerCase().includes(searchText.toLowerCase()))
found = true
})
return Object.values(modelItem.label).some(label =>
label.toLowerCase().includes(searchText.toLowerCase()),
)
})
return found
},
).length,
)
return { ...model, models: filteredModels }
}).filter(model => model.models.length > 0)
return (
<div className='w-[320px] max-h-[480px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg overflow-y-auto'>

View File

@ -4,6 +4,7 @@ import Link from 'next/link'
import { useBoolean } from 'ahooks'
import { useSelectedLayoutSegment } from 'next/navigation'
import { Bars3Icon } from '@heroicons/react/20/solid'
import { useContextSelector } from 'use-context-selector'
import HeaderBillingBtn from '../billing/header-billing-btn'
import AccountDropdown from './account-dropdown'
import AppNav from './app-nav'
@ -14,11 +15,12 @@ import ToolsNav from './tools-nav'
import GithubStar from './github-star'
import LicenseNav from './license-env'
import { WorkspaceProvider } from '@/context/workspace-context'
import { useAppContext } from '@/context/app-context'
import AppContext, { useAppContext } from '@/context/app-context'
import LogoSite from '@/app/components/base/logo/logo-site'
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
import { useProviderContext } from '@/context/provider-context'
import { useModalContext } from '@/context/modal-context'
import { LicenseStatus } from '@/types/feature'
const navClassName = `
flex items-center relative mr-0 sm:mr-3 px-3 h-8 rounded-xl
@ -28,7 +30,7 @@ const navClassName = `
const Header = () => {
const { isCurrentWorkspaceEditor, isCurrentWorkspaceDatasetOperator } = useAppContext()
const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures)
const selectedSegment = useSelectedLayoutSegment()
const media = useBreakpoints()
const isMobile = media === MediaType.mobile
@ -60,7 +62,7 @@ const Header = () => {
<Link href="/apps" className='flex items-center mr-4'>
<LogoSite className='object-contain' />
</Link>
<GithubStar />
{systemFeatures.license.status === LicenseStatus.NONE && <GithubStar />}
</>}
</div>
{isMobile && (
@ -68,7 +70,7 @@ const Header = () => {
<Link href="/apps" className='flex items-center mr-4'>
<LogoSite />
</Link>
<GithubStar />
{systemFeatures.license.status === LicenseStatus.NONE && <GithubStar />}
</div>
)}
{!isMobile && (

View File

@ -48,6 +48,7 @@ const getIcon = (type: BlockEnum, className: string) => {
[BlockEnum.VariableAggregator]: <VariableX className={className} />,
[BlockEnum.Assigner]: <Assigner className={className} />,
[BlockEnum.Tool]: <VariableX className={className} />,
[BlockEnum.IterationStart]: <VariableX className={className} />,
[BlockEnum.Iteration]: <Iteration className={className} />,
[BlockEnum.ParameterExtractor]: <ParameterExtractor className={className} />,
[BlockEnum.DocExtractor]: <DocsExtractor className={className} />,

View File

@ -271,13 +271,18 @@ export const useWorkflowRun = () => {
} as any)
}
else {
if (!iterParallelLogMap.has(data.parallel_run_id))
iterParallelLogMap.set(data.parallel_run_id, [{ ...data, status: NodeRunningStatus.Running } as any])
const nodeId = iterations?.node_id as string
if (!iterParallelLogMap.has(nodeId as string))
iterParallelLogMap.set(iterations?.node_id as string, new Map())
const currentIterLogMap = iterParallelLogMap.get(nodeId)!
if (!currentIterLogMap.has(data.parallel_run_id))
currentIterLogMap.set(data.parallel_run_id, [{ ...data, status: NodeRunningStatus.Running } as any])
else
iterParallelLogMap.get(data.parallel_run_id)!.push({ ...data, status: NodeRunningStatus.Running } as any)
currentIterLogMap.get(data.parallel_run_id)!.push({ ...data, status: NodeRunningStatus.Running } as any)
setIterParallelLogMap(iterParallelLogMap)
if (iterations)
iterations.details = Array.from(iterParallelLogMap.values())
iterations.details = Array.from(currentIterLogMap.values())
}
}))
}
@ -373,7 +378,7 @@ export const useWorkflowRun = () => {
if (iterations && iterations.details) {
const iterRunID = data.execution_metadata?.parallel_mode_run_id
const currIteration = iterParallelLogMap.get(iterRunID)
const currIteration = iterParallelLogMap.get(iterations.node_id)?.get(iterRunID)
const nodeIndex = currIteration?.findIndex(node =>
node.node_id === data.node_id && (
node?.parallel_run_id === data.execution_metadata?.parallel_mode_run_id),
@ -392,7 +397,9 @@ export const useWorkflowRun = () => {
}
}
setIterParallelLogMap(iterParallelLogMap)
iterations.details = Array.from(iterParallelLogMap.values())
const iterLogMap = iterParallelLogMap.get(iterations.node_id)
if (iterLogMap)
iterations.details = Array.from(iterLogMap.values())
}
}))
}

View File

@ -33,6 +33,7 @@ export type Props = {
showFileList?: boolean
onGenerated?: (value: string) => void
showCodeGenerator?: boolean
className?: string
}
export const languageMap = {
@ -67,6 +68,7 @@ const CodeEditor: FC<Props> = ({
showFileList,
onGenerated,
showCodeGenerator = false,
className,
}) => {
const [isFocus, setIsFocus] = React.useState(false)
const [isMounted, setIsMounted] = React.useState(false)
@ -187,7 +189,7 @@ const CodeEditor: FC<Props> = ({
)
return (
<div className={cn(isExpand && 'h-full')}>
<div className={cn(isExpand && 'h-full', className)}>
{noWrapper
? <div className='relative no-wrapper' style={{
height: isExpand ? '100%' : (editorContentHeight) / 2 + CODE_EDITOR_LINE_HEIGHT, // In IDE, the last line can always be in lop line. So there is some blank space in the bottom.

View File

@ -47,7 +47,6 @@ const Field: FC<Props> = ({
triggerClassName='w-4 h-4 ml-1'
/>
)}
</div>
<div className='flex'>
{operations && <div>{operations}</div>}

View File

@ -10,7 +10,7 @@ const ListNoDataPlaceholder: FC<Props> = ({
children,
}) => {
return (
<div className='flex rounded-md bg-gray-50 items-center min-h-[42px] justify-center leading-[18px] text-xs font-normal text-gray-500'>
<div className='flex w-full rounded-[10px] bg-background-section items-center min-h-[42px] justify-center system-xs-regular text-text-tertiary'>
{children}
</div>
)

View File

@ -53,7 +53,7 @@ type Props = {
const MEMORY_DEFAULT: Memory = {
window: { enabled: false, size: WINDOW_SIZE_DEFAULT },
query_prompt_template: '',
query_prompt_template: '{{#sys.query#}}',
}
const MemoryConfig: FC<Props> = ({

View File

@ -0,0 +1,39 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import VarReferenceVars from './var-reference-vars'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import ListEmpty from '@/app/components/base/list-empty'
type Props = {
vars: NodeOutPutVar[]
onChange: (value: ValueSelector, varDetail: Var) => void
itemWidth?: number
}
const AssignedVarReferencePopup: FC<Props> = ({
vars,
onChange,
itemWidth,
}) => {
const { t } = useTranslation()
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
return (
<div className='p-1 bg-components-panel-bg-bur rounded-lg border-[0.5px] border-components-panel-border shadow-lg w-[352px]' >
{(!vars || vars.length === 0)
? <ListEmpty
title={t('workflow.nodes.assigner.noAssignedVars') || ''}
description={t('workflow.nodes.assigner.assignedVarsDescription')}
/>
: <VarReferenceVars
searchBoxClassName='mt-1'
vars={vars}
onChange={onChange}
itemWidth={itemWidth}
isSupportFileVar
/>
}
</div >
)
}
export default React.memo(AssignedVarReferencePopup)

View File

@ -60,6 +60,9 @@ type Props = {
onRemove?: () => void
typePlaceHolder?: string
isSupportFileVar?: boolean
placeholder?: string
minWidth?: number
popupFor?: 'assigned' | 'toAssigned'
}
const VarReferencePicker: FC<Props> = ({
@ -83,6 +86,9 @@ const VarReferencePicker: FC<Props> = ({
onRemove,
typePlaceHolder,
isSupportFileVar = true,
placeholder,
minWidth,
popupFor,
}) => {
const { t } = useTranslation()
const store = useStoreApi()
@ -261,7 +267,7 @@ const VarReferencePicker: FC<Props> = ({
<AddButton onClick={() => { }}></AddButton>
</div>
)
: (<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8', !isSupportConstantValue && 'p-1 rounded-lg bg-gray-100 border', isInTable && 'bg-transparent border-none')}>
: (<div ref={!isSupportConstantValue ? triggerRef : null} className={cn((open || isFocus) ? 'border-gray-300' : 'border-gray-100', 'relative group/wrap flex items-center w-full h-8', !isSupportConstantValue && 'p-1 rounded-lg bg-gray-100 border', isInTable && 'bg-transparent border-none', readonly && 'bg-components-input-bg-disabled')}>
{isSupportConstantValue
? <div onClick={(e) => {
e.stopPropagation()
@ -285,7 +291,7 @@ const VarReferencePicker: FC<Props> = ({
/>
</div>
: (!hasValue && <div className='ml-1.5 mr-1'>
<Variable02 className='w-3.5 h-3.5 text-gray-400' />
<Variable02 className={`w-4 h-4 ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'}`} />
</div>)}
{isConstant
? (
@ -329,17 +335,17 @@ const VarReferencePicker: FC<Props> = ({
{!hasValue && <Variable02 className='w-3.5 h-3.5' />}
{isEnv && <Env className='w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('ml-0.5 text-xs font-medium truncate', (isEnv || isChatVar) && '!text-text-secondary')} title={varName} style={{
<div className={cn('ml-0.5 text-xs font-medium truncate', isEnv && '!text-text-secondary', isChatVar && 'text-util-colors-teal-teal-700')} title={varName} style={{
maxWidth: maxVarNameWidth,
}}>{varName}</div>
</div>
<div className='ml-0.5 text-xs font-normal text-gray-500 capitalize truncate' title={type} style={{
<div className='ml-0.5 capitalize truncate text-text-tertiary text-center system-xs-regular' title={type} style={{
maxWidth: maxTypeWidth,
}}>{type}</div>
{!isValidVar && <RiErrorWarningFill className='ml-0.5 w-3 h-3 text-[#D92D20]' />}
</>
)
: <div className='text-[13px] font-normal text-gray-400'>{t('workflow.common.setVarValuePlaceholder')}</div>}
: <div className={`overflow-hidden ${readonly ? 'text-components-input-text-disabled' : 'text-components-input-text-placeholder'} text-ellipsis system-sm-regular`}>{placeholder ?? t('workflow.common.setVarValuePlaceholder')}</div>}
</div>
</Tooltip>
</div>
@ -378,12 +384,13 @@ const VarReferencePicker: FC<Props> = ({
</WrapElem>
<PortalToFollowElemContent style={{
zIndex: 100,
}}>
}} className='mt-1'>
{!isConstant && (
<VarReferencePopup
vars={outputVars}
popupFor={popupFor}
onChange={handleVarReferenceChange}
itemWidth={isAddBtnTrigger ? 260 : triggerWidth}
itemWidth={isAddBtnTrigger ? 260 : (minWidth || triggerWidth)}
isSupportFileVar={isSupportFileVar}
/>
)}

View File

@ -1,33 +1,64 @@
'use client'
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import VarReferenceVars from './var-reference-vars'
import type { NodeOutPutVar, ValueSelector, Var } from '@/app/components/workflow/types'
import ListEmpty from '@/app/components/base/list-empty'
import { LanguagesSupported } from '@/i18n/language'
import I18n from '@/context/i18n'
type Props = {
vars: NodeOutPutVar[]
popupFor?: 'assigned' | 'toAssigned'
onChange: (value: ValueSelector, varDetail: Var) => void
itemWidth?: number
isSupportFileVar?: boolean
}
const VarReferencePopup: FC<Props> = ({
vars,
popupFor,
onChange,
itemWidth,
isSupportFileVar = true,
}) => {
const { t } = useTranslation()
const { locale } = useContext(I18n)
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
return (
<div className='p-1 bg-white rounded-lg border border-gray-200 shadow-lg space-y-1' style={{
width: itemWidth || 228,
}}>
<VarReferenceVars
searchBoxClassName='mt-1'
vars={vars}
onChange={onChange}
itemWidth={itemWidth}
isSupportFileVar={isSupportFileVar}
/>
{((!vars || vars.length === 0) && popupFor)
? (popupFor === 'toAssigned'
? (
<ListEmpty
title={t('workflow.variableReference.noAvailableVars') || ''}
description={<div className='text-text-tertiary system-xs-regular'>
{t('workflow.variableReference.noVarsForOperation')}
</div>}
/>
)
: (
<ListEmpty
title={t('workflow.variableReference.noAssignedVars') || ''}
description={<div className='text-text-tertiary system-xs-regular'>
{t('workflow.variableReference.assignedVarsDescription')}
<a target='_blank' rel='noopener noreferrer'
className='text-text-accent-secondary'
href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/guides/workflow/variables#conversation-variables' : `https://docs.dify.ai/${locale.toLowerCase()}/guides/workflow/variables#hui-hua-bian-liang`}>{t('workflow.variableReference.conversationVars')}</a>
</div>}
/>
))
: <VarReferenceVars
searchBoxClassName='mt-1'
vars={vars}
onChange={onChange}
itemWidth={itemWidth}
isSupportFileVar={isSupportFileVar}
/>
}
</div >
)
}

View File

@ -24,6 +24,7 @@ import QuestionClassifyDefault from '@/app/components/workflow/nodes/question-cl
import HTTPDefault from '@/app/components/workflow/nodes/http/default'
import ToolDefault from '@/app/components/workflow/nodes/tool/default'
import VariableAssigner from '@/app/components/workflow/nodes/variable-assigner/default'
import Assigner from '@/app/components/workflow/nodes/assigner/default'
import ParameterExtractorDefault from '@/app/components/workflow/nodes/parameter-extractor/default'
import IterationDefault from '@/app/components/workflow/nodes/iteration/default'
import { ssePost } from '@/service/base'
@ -39,6 +40,7 @@ const { checkValid: checkQuestionClassifyValid } = QuestionClassifyDefault
const { checkValid: checkHttpValid } = HTTPDefault
const { checkValid: checkToolValid } = ToolDefault
const { checkValid: checkVariableAssignerValid } = VariableAssigner
const { checkValid: checkAssignerValid } = Assigner
const { checkValid: checkParameterExtractorValid } = ParameterExtractorDefault
const { checkValid: checkIterationValid } = IterationDefault
@ -51,7 +53,7 @@ const checkValidFns: Record<BlockEnum, Function> = {
[BlockEnum.QuestionClassifier]: checkQuestionClassifyValid,
[BlockEnum.HttpRequest]: checkHttpValid,
[BlockEnum.Tool]: checkToolValid,
[BlockEnum.VariableAssigner]: checkVariableAssignerValid,
[BlockEnum.VariableAssigner]: checkAssignerValid,
[BlockEnum.VariableAggregator]: checkVariableAssignerValid,
[BlockEnum.ParameterExtractor]: checkParameterExtractorValid,
[BlockEnum.Iteration]: checkIterationValid,

View File

@ -0,0 +1,128 @@
import type { FC } from 'react'
import { useState } from 'react'
import {
RiArrowDownSLine,
RiCheckLine,
} from '@remixicon/react'
import classNames from 'classnames'
import { useTranslation } from 'react-i18next'
import type { WriteMode } from '../types'
import { getOperationItems } from '../utils'
import {
PortalToFollowElem,
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import type { VarType } from '@/app/components/workflow/types'
import Divider from '@/app/components/base/divider'
type Item = {
value: string | number
name: string
}
type OperationSelectorProps = {
value: string | number
onSelect: (value: Item) => void
placeholder?: string
disabled?: boolean
className?: string
popupClassName?: string
assignedVarType?: VarType
writeModeTypes?: WriteMode[]
writeModeTypesArr?: WriteMode[]
writeModeTypesNum?: WriteMode[]
}
const i18nPrefix = 'workflow.nodes.assigner'
const OperationSelector: FC<OperationSelectorProps> = ({
value,
onSelect,
disabled = false,
className,
popupClassName,
assignedVarType,
writeModeTypes,
writeModeTypesArr,
writeModeTypesNum,
}) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false)
const items = getOperationItems(assignedVarType, writeModeTypes, writeModeTypesArr, writeModeTypesNum)
const selectedItem = items.find(item => item.value === value)
return (
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
placement='bottom-start'
offset={4}
>
<PortalToFollowElemTrigger
onClick={() => !disabled && setOpen(v => !v)}
>
<div
className={classNames(
'flex items-center px-2 py-1 gap-0.5 rounded-lg bg-components-input-bg-normal',
disabled ? 'cursor-not-allowed !bg-components-input-bg-disabled' : 'cursor-pointer hover:bg-state-base-hover-alt',
open && 'bg-state-base-hover-alt',
className,
)}
>
<div className='flex p-1 items-center'>
<span
className={`truncate overflow-hidden text-ellipsis system-sm-regular
${selectedItem ? 'text-components-input-text-filled' : 'text-components-input-text-disabled'}`}
>
{selectedItem?.name ? t(`${i18nPrefix}.operations.${selectedItem?.name}`) : t(`${i18nPrefix}.operations.title`)}
</span>
</div>
<RiArrowDownSLine className={`h-4 w-4 text-text-quaternary ${disabled && 'text-components-input-text-placeholder'} ${open && 'text-text-secondary'}`} />
</div>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className={`z-20 ${popupClassName}`}>
<div className='flex w-[140px] flex-col items-start rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-lg'>
<div className='flex p-1 flex-col items-start self-stretch'>
<div className='flex px-3 pt-1 pb-0.5 items-start self-stretch'>
<div className='flex grow text-text-tertiary system-xs-medium-uppercase'>{t(`${i18nPrefix}.operations.title`)}</div>
</div>
{items.map(item => (
item.value === 'divider'
? (
<Divider key="divider" className="my-1" />
)
: (
<div
key={item.value}
className={classNames(
'flex items-center px-2 py-1 gap-1 self-stretch rounded-lg',
'cursor-pointer hover:bg-state-base-hover',
)}
onClick={() => {
onSelect(item)
setOpen(false)
}}
>
<div className='flex min-h-5 px-1 items-center gap-1 grow'>
<span className={'flex flex-grow text-text-secondary system-sm-medium'}>{t(`${i18nPrefix}.operations.${item.name}`)}</span>
</div>
{item.value === value && (
<div className='flex justify-center items-center'>
<RiCheckLine className='h-4 w-4 text-text-accent' />
</div>
)}
</div>
)
))}
</div>
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>
)
}
export default OperationSelector

View File

@ -0,0 +1,227 @@
'use client'
import type { FC } from 'react'
import { useTranslation } from 'react-i18next'
import React, { useCallback } from 'react'
import produce from 'immer'
import { RiDeleteBinLine } from '@remixicon/react'
import OperationSelector from '../operation-selector'
import { AssignerNodeInputType, WriteMode } from '../../types'
import type { AssignerNodeOperation } from '../../types'
import ListNoDataPlaceholder from '@/app/components/workflow/nodes/_base/components/list-no-data-placeholder'
import VarReferencePicker from '@/app/components/workflow/nodes/_base/components/variable/var-reference-picker'
import type { ValueSelector, Var, VarType } from '@/app/components/workflow/types'
import { CodeLanguage } from '@/app/components/workflow/nodes/code/types'
import ActionButton from '@/app/components/base/action-button'
import Input from '@/app/components/base/input'
import Textarea from '@/app/components/base/textarea'
import CodeEditor from '@/app/components/workflow/nodes/_base/components/editor/code-editor'
type Props = {
readonly: boolean
nodeId: string
list: AssignerNodeOperation[]
onChange: (list: AssignerNodeOperation[], value?: ValueSelector) => void
onOpen?: (index: number) => void
filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean
filterToAssignedVar?: (payload: Var, assignedVarType: VarType, write_mode: WriteMode) => boolean
getAssignedVarType?: (valueSelector: ValueSelector) => VarType
getToAssignedVarType?: (assignedVarType: VarType, write_mode: WriteMode) => VarType
writeModeTypes?: WriteMode[]
writeModeTypesArr?: WriteMode[]
writeModeTypesNum?: WriteMode[]
}
const VarList: FC<Props> = ({
readonly,
nodeId,
list,
onChange,
onOpen = () => { },
filterVar,
filterToAssignedVar,
getAssignedVarType,
getToAssignedVarType,
writeModeTypes,
writeModeTypesArr,
writeModeTypesNum,
}) => {
const { t } = useTranslation()
const handleAssignedVarChange = useCallback((index: number) => {
return (value: ValueSelector | string) => {
const newList = produce(list, (draft) => {
draft[index].variable_selector = value as ValueSelector
draft[index].operation = WriteMode.overwrite
draft[index].value = undefined
})
onChange(newList, value as ValueSelector)
}
}, [list, onChange])
const handleOperationChange = useCallback((index: number) => {
return (item: { value: string | number }) => {
const newList = produce(list, (draft) => {
draft[index].operation = item.value as WriteMode
draft[index].value = '' // Clear value when operation changes
if (item.value === WriteMode.set || item.value === WriteMode.increment || item.value === WriteMode.decrement
|| item.value === WriteMode.multiply || item.value === WriteMode.divide)
draft[index].input_type = AssignerNodeInputType.constant
else
draft[index].input_type = AssignerNodeInputType.variable
})
onChange(newList)
}
}, [list, onChange])
const handleToAssignedVarChange = useCallback((index: number) => {
return (value: ValueSelector | string | number) => {
const newList = produce(list, (draft) => {
draft[index].value = value as ValueSelector
})
onChange(newList, value as ValueSelector)
}
}, [list, onChange])
const handleVarRemove = useCallback((index: number) => {
return () => {
const newList = produce(list, (draft) => {
draft.splice(index, 1)
})
onChange(newList)
}
}, [list, onChange])
const handleOpen = useCallback((index: number) => {
return () => onOpen(index)
}, [onOpen])
const handleFilterToAssignedVar = useCallback((index: number) => {
return (payload: Var, valueSelector: ValueSelector) => {
const item = list[index]
const assignedVarType = item.variable_selector ? getAssignedVarType?.(item.variable_selector) : undefined
if (!filterToAssignedVar || !item.variable_selector || !assignedVarType || !item.operation)
return true
return filterToAssignedVar(
payload,
assignedVarType,
item.operation,
)
}
}, [list, filterToAssignedVar, getAssignedVarType])
if (list.length === 0) {
return (
<ListNoDataPlaceholder>
{t('workflow.nodes.assigner.noVarTip')}
</ListNoDataPlaceholder>
)
}
return (
<div className='flex flex-col items-start gap-4 self-stretch'>
{list.map((item, index) => {
const assignedVarType = item.variable_selector ? getAssignedVarType?.(item.variable_selector) : undefined
const toAssignedVarType = (assignedVarType && item.operation && getToAssignedVarType)
? getToAssignedVarType(assignedVarType, item.operation)
: undefined
return (
<div className='flex items-start gap-1 self-stretch' key={index}>
<div className='flex flex-col items-start gap-1 flex-grow'>
<div className='flex items-center gap-1 self-stretch'>
<VarReferencePicker
readonly={readonly}
nodeId={nodeId}
isShowNodeName
value={item.variable_selector || []}
onChange={handleAssignedVarChange(index)}
onOpen={handleOpen(index)}
filterVar={filterVar}
placeholder={t('workflow.nodes.assigner.selectAssignedVariable') as string}
minWidth={352}
popupFor='assigned'
className='w-full'
/>
<OperationSelector
value={item.operation}
placeholder='Operation'
disabled={!item.variable_selector || item.variable_selector.length === 0}
onSelect={handleOperationChange(index)}
assignedVarType={assignedVarType}
writeModeTypes={writeModeTypes}
writeModeTypesArr={writeModeTypesArr}
writeModeTypesNum={writeModeTypesNum}
/>
</div>
{item.operation !== WriteMode.clear && item.operation !== WriteMode.set
&& !writeModeTypesNum?.includes(item.operation)
&& (
<VarReferencePicker
readonly={readonly || !item.variable_selector || !item.operation}
nodeId={nodeId}
isShowNodeName
value={item.value}
onChange={handleToAssignedVarChange(index)}
filterVar={handleFilterToAssignedVar(index)}
valueTypePlaceHolder={toAssignedVarType}
placeholder={t('workflow.nodes.assigner.setParameter') as string}
minWidth={352}
popupFor='toAssigned'
className='w-full'
/>
)
}
{item.operation === WriteMode.set && assignedVarType && (
<>
{assignedVarType === 'number' && (
<Input
type="number"
value={item.value as number}
onChange={e => handleToAssignedVarChange(index)(Number(e.target.value))}
className='w-full'
/>
)}
{assignedVarType === 'string' && (
<Textarea
value={item.value as string}
onChange={e => handleToAssignedVarChange(index)(e.target.value)}
className='w-full'
/>
)}
{assignedVarType === 'object' && (
<CodeEditor
value={item.value as string}
language={CodeLanguage.json}
onChange={value => handleToAssignedVarChange(index)(value)}
className='w-full'
readOnly={readonly}
/>
)}
</>
)}
{writeModeTypesNum?.includes(item.operation)
&& <Input
type="number"
value={item.value as number}
onChange={e => handleToAssignedVarChange(index)(Number(e.target.value))}
placeholder="Enter number value..."
className='w-full'
/>
}
</div>
<ActionButton
size='l'
className='flex-shrink-0 group hover:!bg-state-destructive-hover'
onClick={handleVarRemove(index)}
>
<RiDeleteBinLine className='text-text-tertiary w-4 h-4 group-hover:text-text-destructive' />
</ActionButton>
</div>
)
},
)}
</div>
)
}
export default React.memo(VarList)

View File

@ -0,0 +1,39 @@
import { useCallback } from 'react'
import produce from 'immer'
import type { AssignerNodeOperation, AssignerNodeType } from '../../types'
import { AssignerNodeInputType, WriteMode } from '../../types'
type Params = {
id: string
inputs: AssignerNodeType
setInputs: (newInputs: AssignerNodeType) => void
}
function useVarList({
inputs,
setInputs,
}: Params) {
const handleVarListChange = useCallback((newList: AssignerNodeOperation[]) => {
const newInputs = produce(inputs, (draft) => {
draft.items = newList
})
setInputs(newInputs)
}, [inputs, setInputs])
const handleAddVariable = useCallback(() => {
const newInputs = produce(inputs, (draft) => {
draft.items.push({
variable_selector: [],
input_type: AssignerNodeInputType.constant,
operation: WriteMode.overwrite,
value: '',
})
})
setInputs(newInputs)
}, [inputs, setInputs])
return {
handleVarListChange,
handleAddVariable,
}
}
export default useVarList

View File

@ -6,9 +6,8 @@ const i18nPrefix = 'workflow.errorMsg'
const nodeDefault: NodeDefault<AssignerNodeType> = {
defaultValue: {
assigned_variable_selector: [],
write_mode: WriteMode.Overwrite,
input_variable_selector: [],
version: '2',
items: [],
},
getAvailablePrevNodes(isChatMode: boolean) {
const nodes = isChatMode
@ -23,18 +22,25 @@ const nodeDefault: NodeDefault<AssignerNodeType> = {
checkValid(payload: AssignerNodeType, t: any) {
let errorMessages = ''
const {
assigned_variable_selector: assignedVarSelector,
write_mode: writeMode,
input_variable_selector: toAssignerVarSelector,
items: operationItems,
} = payload
if (!errorMessages && !assignedVarSelector?.length)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') })
operationItems?.forEach((value) => {
if (!errorMessages && !value.variable_selector?.length)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.assignedVariable') })
if (!errorMessages && writeMode !== WriteMode.Clear) {
if (!toAssignerVarSelector?.length)
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.variable') })
}
if (!errorMessages && value.operation !== WriteMode.clear) {
if (value.operation === WriteMode.set || value.operation === WriteMode.increment
|| value.operation === WriteMode.decrement || value.operation === WriteMode.multiply
|| value.operation === WriteMode.divide) {
if (!value.value && typeof value.value !== 'number')
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.variable') })
}
else if (!value.value?.length) {
errorMessages = t(`${i18nPrefix}.fieldRequired`, { field: t('workflow.nodes.assigner.variable') })
}
}
})
return {
isValid: !errorMessages,

View File

@ -0,0 +1,70 @@
import { useCallback } from 'react'
import {
useNodes,
} from 'reactflow'
import { uniqBy } from 'lodash-es'
import {
useIsChatMode,
useWorkflow,
useWorkflowVariables,
} from '../../hooks'
import type {
Node,
Var,
} from '../../types'
import { AssignerNodeInputType, WriteMode } from './types'
export const useGetAvailableVars = () => {
const nodes: Node[] = useNodes()
const { getBeforeNodesInSameBranchIncludeParent } = useWorkflow()
const { getNodeAvailableVars } = useWorkflowVariables()
const isChatMode = useIsChatMode()
const getAvailableVars = useCallback((nodeId: string, handleId: string, filterVar: (v: Var) => boolean, hideEnv = false) => {
const availableNodes: Node[] = []
const currentNode = nodes.find(node => node.id === nodeId)!
if (!currentNode)
return []
const beforeNodes = getBeforeNodesInSameBranchIncludeParent(nodeId)
availableNodes.push(...beforeNodes)
const parentNode = nodes.find(node => node.id === currentNode.parentId)
if (hideEnv) {
return getNodeAvailableVars({
parentNode,
beforeNodes: uniqBy(availableNodes, 'id').filter(node => node.id !== nodeId),
isChatMode,
hideEnv,
hideChatVar: hideEnv,
filterVar,
})
.map(node => ({
...node,
vars: node.isStartNode ? node.vars.filter(v => !v.variable.startsWith('sys.')) : node.vars,
}))
.filter(item => item.vars.length > 0)
}
return getNodeAvailableVars({
parentNode,
beforeNodes: uniqBy(availableNodes, 'id').filter(node => node.id !== nodeId),
isChatMode,
filterVar,
})
}, [nodes, getBeforeNodesInSameBranchIncludeParent, getNodeAvailableVars, isChatMode])
return getAvailableVars
}
export const useHandleAddOperationItem = () => {
return useCallback((list: any[]) => {
const newItem = {
variable_selector: [],
write_mode: WriteMode.overwrite,
input_type: AssignerNodeInputType.variable,
value: '',
}
return [...list, newItem]
}, [])
}

View File

@ -15,31 +15,71 @@ const NodeComponent: FC<NodeProps<AssignerNodeType>> = ({
const { t } = useTranslation()
const nodes: Node[] = useNodes()
const { assigned_variable_selector: variable, write_mode: writeMode } = data
if (data.version === '2') {
const { items: operationItems } = data
const validOperationItems = operationItems?.filter(item =>
item.variable_selector && item.variable_selector.length > 0,
) || []
if (validOperationItems.length === 0) {
return (
<div className='relative flex flex-col px-3 py-1 gap-0.5 items-start self-stretch'>
<div className='flex flex-col items-start gap-1 self-stretch'>
<div className='flex px-[5px] py-1 items-center gap-1 self-stretch rounded-md bg-workflow-block-parma-bg'>
<div className='flex-1 text-text-tertiary system-xs-medium'>{t(`${i18nPrefix}.varNotSet`)}</div>
</div>
</div>
</div>
)
}
return (
<div className='relative flex flex-col px-3 py-1 gap-0.5 items-start self-stretch'>
{operationItems.map((value, index) => {
const variable = value.variable_selector
if (!variable || variable.length === 0)
return null
const isSystem = isSystemVar(variable)
const isEnv = isENV(variable)
const isChatVar = isConversationVar(variable)
const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0])
const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.')
return (
<NodeVariableItem
key={index}
node={node as Node}
isEnv={isEnv}
isChatVar={isChatVar}
writeMode={value.operation}
varName={varName}
className='bg-workflow-block-parma-bg'
/>
)
})}
</div>
)
}
// Legacy version
const { assigned_variable_selector: variable, write_mode: writeMode } = data as any
if (!variable || variable.length === 0)
return null
const isSystem = isSystemVar(variable)
const isEnv = isENV(variable)
const isChatVar = isConversationVar(variable)
const node = isSystem ? nodes.find(node => node.data.type === BlockEnum.Start) : nodes.find(node => node.id === variable[0])
const varName = isSystem ? `sys.${variable[variable.length - 1]}` : variable.slice(1).join('.')
return (
<div className='relative px-3'>
<div className='mb-1 system-2xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.assignedVariable`)}</div>
<div className='relative flex flex-col px-3 py-1 gap-0.5 items-start self-stretch'>
<NodeVariableItem
node={node as Node}
isEnv={isEnv}
isChatVar={isChatVar}
varName={varName}
writeMode={writeMode}
className='bg-workflow-block-parma-bg'
/>
<div className='my-2 flex justify-between items-center h-[22px] px-[5px] bg-workflow-block-parma-bg radius-sm'>
<div className='system-xs-medium-uppercase text-text-tertiary'>{t(`${i18nPrefix}.writeMode`)}</div>
<div className='system-xs-medium text-text-secondary'>{t(`${i18nPrefix}.${writeMode}`)}</div>
</div>
</div>
)
}

View File

@ -1,15 +1,15 @@
import type { FC } from 'react'
import React from 'react'
import { useTranslation } from 'react-i18next'
import VarReferencePicker from '../_base/components/variable/var-reference-picker'
import OptionCard from '../_base/components/option-card'
import {
RiAddLine,
} from '@remixicon/react'
import VarList from './components/var-list'
import useConfig from './use-config'
import { WriteMode } from './types'
import type { AssignerNodeType } from './types'
import Field from '@/app/components/workflow/nodes/_base/components/field'
import { useHandleAddOperationItem } from './hooks'
import ActionButton from '@/app/components/base/action-button'
import { type NodePanelProps } from '@/app/components/workflow/types'
import cn from '@/utils/classnames'
const i18nPrefix = 'workflow.nodes.assigner'
@ -18,67 +18,48 @@ const Panel: FC<NodePanelProps<AssignerNodeType>> = ({
data,
}) => {
const { t } = useTranslation()
const handleAddOperationItem = useHandleAddOperationItem()
const {
readOnly,
inputs,
handleAssignedVarChanges,
isSupportAppend,
handleOperationListChanges,
getAssignedVarType,
getToAssignedVarType,
writeModeTypesNum,
writeModeTypesArr,
writeModeTypes,
handleWriteModeChange,
filterAssignedVar,
filterToAssignedVar,
handleToAssignedVarChange,
toAssignedVarType,
} = useConfig(id, data)
const handleAddOperation = () => {
const newList = handleAddOperationItem(inputs.items || [])
handleOperationListChanges(newList)
}
return (
<div className='mt-2'>
<div className='px-4 pb-4 space-y-4'>
<Field
title={t(`${i18nPrefix}.assignedVariable`)}
>
<VarReferencePicker
readonly={readOnly}
nodeId={id}
isShowNodeName
value={inputs.assigned_variable_selector || []}
onChange={handleAssignedVarChanges}
filterVar={filterAssignedVar}
/>
</Field>
<Field
title={t(`${i18nPrefix}.writeMode`)}
>
<div className={cn('grid gap-2 grid-cols-3')}>
{writeModeTypes.map(type => (
<OptionCard
key={type}
title={t(`${i18nPrefix}.${type}`)}
onSelect={handleWriteModeChange(type)}
selected={inputs.write_mode === type}
disabled={!isSupportAppend && type === WriteMode.Append}
tooltip={type === WriteMode.Append ? t(`${i18nPrefix}.writeModeTip`)! : undefined}
/>
))}
</div>
</Field>
{inputs.write_mode !== WriteMode.Clear && (
<Field
title={t(`${i18nPrefix}.setVariable`)}
>
<VarReferencePicker
readonly={readOnly}
nodeId={id}
isShowNodeName
value={inputs.input_variable_selector || []}
onChange={handleToAssignedVarChange}
filterVar={filterToAssignedVar}
valueTypePlaceHolder={toAssignedVarType}
/>
</Field>
)}
<div className='flex py-2 flex-col items-start self-stretch'>
<div className='flex flex-col justify-center items-start gap-1 px-4 py-2 w-full self-stretch'>
<div className='flex items-start gap-2 self-stretch'>
<div className='flex flex-col justify-center items-start flex-grow text-text-secondary system-sm-semibold-uppercase'>{t(`${i18nPrefix}.variables`)}</div>
<ActionButton onClick={handleAddOperation}>
<RiAddLine className='w-4 h-4 shrink-0 text-text-tertiary' />
</ActionButton>
</div>
<VarList
readonly={readOnly}
nodeId={id}
list={inputs.items || []}
onChange={(newList) => {
handleOperationListChanges(newList)
}}
filterVar={filterAssignedVar}
filterToAssignedVar={filterToAssignedVar}
getAssignedVarType={getAssignedVarType}
writeModeTypes={writeModeTypes}
writeModeTypesArr={writeModeTypesArr}
writeModeTypesNum={writeModeTypesNum}
getToAssignedVarType={getToAssignedVarType}
/>
</div>
</div>
)

View File

@ -1,13 +1,30 @@
import type { CommonNodeType, ValueSelector } from '@/app/components/workflow/types'
export enum WriteMode {
Overwrite = 'over-write',
Append = 'append',
Clear = 'clear',
overwrite = 'over-write',
clear = 'clear',
append = 'append',
extend = 'extend',
set = 'set',
increment = '+=',
decrement = '-=',
multiply = '*=',
divide = '/=',
}
export enum AssignerNodeInputType {
variable = 'variable',
constant = 'constant',
}
export type AssignerNodeOperation = {
variable_selector: ValueSelector
input_type: AssignerNodeInputType
operation: WriteMode
value: any
}
export type AssignerNodeType = CommonNodeType & {
assigned_variable_selector: ValueSelector
write_mode: WriteMode
input_variable_selector: ValueSelector
version?: '1' | '2'
items: AssignerNodeOperation[]
}

View File

@ -1,10 +1,12 @@
import { useCallback, useMemo } from 'react'
import produce from 'immer'
import { useStoreApi } from 'reactflow'
import { isEqual } from 'lodash-es'
import { VarType } from '../../types'
import type { ValueSelector, Var } from '../../types'
import { type AssignerNodeType, WriteMode } from './types'
import { WriteMode } from './types'
import type { AssignerNodeOperation, AssignerNodeType } from './types'
import { useGetAvailableVars } from './hooks'
import { convertV1ToV2 } from './utils'
import useNodeCrud from '@/app/components/workflow/nodes/_base/hooks/use-node-crud'
import {
useIsChatMode,
@ -13,9 +15,20 @@ import {
useWorkflowVariables,
} from '@/app/components/workflow/hooks'
const useConfig = (id: string, payload: AssignerNodeType) => {
const useConfig = (id: string, rawPayload: AssignerNodeType) => {
const payload = useMemo(() => convertV1ToV2(rawPayload), [rawPayload])
const { nodesReadOnly: readOnly } = useNodesReadOnly()
const isChatMode = useIsChatMode()
const getAvailableVars = useGetAvailableVars()
const filterVar = (varType: VarType) => {
return (v: Var) => {
if (varType === VarType.any)
return true
if (v.type === VarType.any)
return true
return v.type === varType
}
}
const store = useStoreApi()
const { getBeforeNodesInSameBranch } = useWorkflow()
@ -30,59 +43,41 @@ const useConfig = (id: string, payload: AssignerNodeType) => {
return getBeforeNodesInSameBranch(id)
}, [getBeforeNodesInSameBranch, id])
const { inputs, setInputs } = useNodeCrud<AssignerNodeType>(id, payload)
const newSetInputs = useCallback((newInputs: AssignerNodeType) => {
const finalInputs = produce(newInputs, (draft) => {
if (draft.version !== '2')
draft.version = '2'
})
setInputs(finalInputs)
}, [setInputs])
const { getCurrentVariableType } = useWorkflowVariables()
const assignedVarType = getCurrentVariableType({
parentNode: iterationNode,
valueSelector: inputs.assigned_variable_selector || [],
availableNodes,
isChatMode,
isConstant: false,
})
const isSupportAppend = useCallback((varType: VarType) => {
return [VarType.arrayString, VarType.arrayNumber, VarType.arrayObject].includes(varType)
}, [])
const isCurrSupportAppend = useMemo(() => isSupportAppend(assignedVarType), [assignedVarType, isSupportAppend])
const handleAssignedVarChanges = useCallback((variable: ValueSelector | string) => {
const newInputs = produce(inputs, (draft) => {
draft.assigned_variable_selector = variable as ValueSelector
draft.input_variable_selector = []
const newVarType = getCurrentVariableType({
parentNode: iterationNode,
valueSelector: draft.assigned_variable_selector || [],
availableNodes,
isChatMode,
isConstant: false,
})
if (inputs.write_mode === WriteMode.Append && !isSupportAppend(newVarType))
draft.write_mode = WriteMode.Overwrite
const getAssignedVarType = useCallback((valueSelector: ValueSelector) => {
return getCurrentVariableType({
parentNode: iterationNode,
valueSelector: valueSelector || [],
availableNodes,
isChatMode,
isConstant: false,
})
setInputs(newInputs)
}, [inputs, setInputs, getCurrentVariableType, iterationNode, availableNodes, isChatMode, isSupportAppend])
}, [getCurrentVariableType, iterationNode, availableNodes, isChatMode])
const writeModeTypes = [WriteMode.Overwrite, WriteMode.Append, WriteMode.Clear]
const handleOperationListChanges = useCallback((items: AssignerNodeOperation[]) => {
const newInputs = produce(inputs, (draft) => {
draft.items = [...items]
})
newSetInputs(newInputs)
}, [inputs, newSetInputs])
const handleWriteModeChange = useCallback((writeMode: WriteMode) => {
return () => {
const newInputs = produce(inputs, (draft) => {
draft.write_mode = writeMode
if (inputs.write_mode === WriteMode.Clear)
draft.input_variable_selector = []
})
setInputs(newInputs)
}
}, [inputs, setInputs])
const writeModeTypesArr = [WriteMode.overwrite, WriteMode.clear, WriteMode.append, WriteMode.extend]
const writeModeTypes = [WriteMode.overwrite, WriteMode.clear, WriteMode.set]
const writeModeTypesNum = [WriteMode.increment, WriteMode.decrement, WriteMode.multiply, WriteMode.divide]
const toAssignedVarType = useMemo(() => {
const { write_mode } = inputs
if (write_mode === WriteMode.Overwrite)
const getToAssignedVarType = useCallback((assignedVarType: VarType, write_mode: WriteMode) => {
if (write_mode === WriteMode.overwrite || write_mode === WriteMode.increment || write_mode === WriteMode.decrement
|| write_mode === WriteMode.multiply || write_mode === WriteMode.divide || write_mode === WriteMode.extend)
return assignedVarType
if (write_mode === WriteMode.Append) {
if (write_mode === WriteMode.append) {
if (assignedVarType === VarType.arrayString)
return VarType.string
if (assignedVarType === VarType.arrayNumber)
@ -91,20 +86,18 @@ const useConfig = (id: string, payload: AssignerNodeType) => {
return VarType.object
}
return VarType.string
}, [assignedVarType, inputs])
}, [])
const filterAssignedVar = useCallback((varPayload: Var, selector: ValueSelector) => {
return selector.join('.').startsWith('conversation')
}, [])
const filterToAssignedVar = useCallback((varPayload: Var, selector: ValueSelector) => {
if (isEqual(selector, inputs.assigned_variable_selector))
return false
if (inputs.write_mode === WriteMode.Overwrite) {
const filterToAssignedVar = useCallback((varPayload: Var, assignedVarType: VarType, write_mode: WriteMode) => {
if (write_mode === WriteMode.overwrite || write_mode === WriteMode.extend || write_mode === WriteMode.increment
|| write_mode === WriteMode.decrement || write_mode === WriteMode.multiply || write_mode === WriteMode.divide) {
return varPayload.type === assignedVarType
}
else if (inputs.write_mode === WriteMode.Append) {
else if (write_mode === WriteMode.append) {
switch (assignedVarType) {
case VarType.arrayString:
return varPayload.type === VarType.string
@ -117,27 +110,21 @@ const useConfig = (id: string, payload: AssignerNodeType) => {
}
}
return true
}, [inputs.assigned_variable_selector, inputs.write_mode, assignedVarType])
const handleToAssignedVarChange = useCallback((value: ValueSelector | string) => {
const newInputs = produce(inputs, (draft) => {
draft.input_variable_selector = value as ValueSelector
})
setInputs(newInputs)
}, [inputs, setInputs])
}, [])
return {
readOnly,
inputs,
handleAssignedVarChanges,
assignedVarType,
isSupportAppend: isCurrSupportAppend,
handleOperationListChanges,
getAssignedVarType,
getToAssignedVarType,
writeModeTypes,
handleWriteModeChange,
writeModeTypesArr,
writeModeTypesNum,
filterAssignedVar,
filterToAssignedVar,
handleToAssignedVarChange,
toAssignedVarType,
getAvailableVars,
filterVar,
}
}

View File

@ -1,5 +1,83 @@
import type { AssignerNodeType } from './types'
import { AssignerNodeInputType, WriteMode } from './types'
export const checkNodeValid = (payload: AssignerNodeType) => {
return true
}
export const formatOperationName = (type: string) => {
if (type === 'over-write')
return 'Overwrite'
return type.charAt(0).toUpperCase() + type.slice(1)
}
type Item = {
value: string | number
name: string
}
export const getOperationItems = (
assignedVarType?: string,
writeModeTypes?: WriteMode[],
writeModeTypesArr?: WriteMode[],
writeModeTypesNum?: WriteMode[],
): Item[] => {
if (assignedVarType?.startsWith('array') && writeModeTypesArr) {
return writeModeTypesArr.map(type => ({
value: type,
name: type,
}))
}
if (assignedVarType === 'number' && writeModeTypes && writeModeTypesNum) {
return [
...writeModeTypes.map(type => ({
value: type,
name: type,
})),
{ value: 'divider', name: 'divider' } as Item,
...writeModeTypesNum.map(type => ({
value: type,
name: type,
})),
]
}
if (writeModeTypes && ['string', 'object'].includes(assignedVarType || '')) {
return writeModeTypes.map(type => ({
value: type,
name: type,
}))
}
return []
}
const convertOldWriteMode = (oldMode: string): WriteMode => {
switch (oldMode) {
case 'over-write':
return WriteMode.overwrite
case 'append':
return WriteMode.append
case 'clear':
return WriteMode.clear
default:
return WriteMode.overwrite
}
}
export const convertV1ToV2 = (payload: any): AssignerNodeType => {
if (payload.version === '2' && payload.items)
return payload as AssignerNodeType
return {
version: '2',
items: [{
variable_selector: payload.assigned_variable_selector || [],
input_type: AssignerNodeInputType.variable,
operation: convertOldWriteMode(payload.write_mode),
value: payload.input_variable_selector || [],
}],
...payload,
}
}

View File

@ -173,7 +173,8 @@ const InputVarList: FC<Props> = ({
value={varInput?.type === VarKindType.constant ? (varInput?.value || '') : (varInput?.value || [])}
onChange={handleNotMixedTypeChange(variable)}
onOpen={handleOpen(index)}
defaultVarKindType={VarKindType.variable}
defaultVarKindType={isNumber ? VarKindType.constant : VarKindType.variable}
isSupportConstantValue={isSupportConstantValue}
filterVar={isNumber ? filterVar : undefined}
availableVars={isSelect ? availableVars : undefined}
schema={schema}

View File

@ -1,9 +1,11 @@
import { memo } from 'react'
import { useTranslation } from 'react-i18next'
import cn from '@/utils/classnames'
import { VarBlockIcon } from '@/app/components/workflow/block-icon'
import { Line3 } from '@/app/components/base/icons/src/public/common'
import { Variable02 } from '@/app/components/base/icons/src/vender/solid/development'
import { BubbleX, Env } from '@/app/components/base/icons/src/vender/line/others'
import Badge from '@/app/components/base/badge'
import type { Node } from '@/app/components/workflow/types'
import { BlockEnum } from '@/app/components/workflow/types'
@ -12,20 +14,26 @@ type NodeVariableItemProps = {
isChatVar: boolean
node: Node
varName: string
writeMode?: string
showBorder?: boolean
className?: string
}
const i18nPrefix = 'workflow.nodes.assigner'
const NodeVariableItem = ({
isEnv,
isChatVar,
node,
varName,
writeMode,
showBorder,
className,
}: NodeVariableItemProps) => {
const { t } = useTranslation()
return (
<div className={cn(
'relative flex items-center mt-0.5 h-6 bg-gray-100 rounded-md px-1 text-xs font-normal text-gray-700',
'relative flex items-center p-[3px] pl-[5px] gap-1 self-stretch rounded-md bg-workflow-block-param-bg',
showBorder && '!bg-black/[0.02]',
className,
)}>
@ -41,11 +49,19 @@ const NodeVariableItem = ({
<Line3 className='mr-0.5'></Line3>
</div>
)}
<div className='flex items-center text-primary-600'>
<div className='flex items-center text-primary-600 w-full'>
{!isEnv && !isChatVar && <Variable02 className='shrink-0 w-3.5 h-3.5 text-primary-500' />}
{isEnv && <Env className='shrink-0 w-3.5 h-3.5 text-util-colors-violet-violet-600' />}
{isChatVar && <BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />}
<div className={cn('max-w-[75px] truncate ml-0.5 text-xs font-medium', (isEnv || isChatVar) && 'text-gray-900')} title={varName}>{varName}</div>
{!isChatVar && <div className={cn('max-w-[75px] truncate ml-0.5 system-xs-medium overflow-hidden text-ellipsis', isEnv && 'text-gray-900')} title={varName}>{varName}</div>}
{isChatVar
&& <div className='flex items-center w-full gap-1'>
<div className='flex h-[18px] min-w-[18px] items-center gap-0.5 flex-1'>
<BubbleX className='w-3.5 h-3.5 text-util-colors-teal-teal-700' />
<div className='max-w-[75px] truncate ml-0.5 system-xs-medium overflow-hidden text-ellipsis text-util-colors-teal-teal-700'>{varName}</div>
</div>
{writeMode && <Badge className='shrink-0' text={t(`${i18nPrefix}.operations.${writeMode}`)} />}
</div>
}
</div>
</div>
)

View File

@ -62,7 +62,7 @@ const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getRe
const formatNodeList = useCallback((list: NodeTracing[]) => {
const allItems = [...list].reverse()
const result: NodeTracing[] = []
const groupMap = new Map<string, NodeTracing[]>()
const nodeGroupMap = new Map<string, Map<string, NodeTracing[]>>()
const processIterationNode = (item: NodeTracing) => {
result.push({
@ -70,11 +70,19 @@ const RunPanel: FC<RunProps> = ({ hideResult, activeTab = 'RESULT', runID, getRe
details: [],
})
}
const updateParallelModeGroup = (runId: string, item: NodeTracing, iterationNode: NodeTracing) => {
if (!nodeGroupMap.has(iterationNode.node_id))
nodeGroupMap.set(iterationNode.node_id, new Map())
const groupMap = nodeGroupMap.get(iterationNode.node_id)!
if (!groupMap.has(runId))
groupMap.set(runId, [item])
else
groupMap.get(runId)!.push(item)
if (item.status === 'failed') {
iterationNode.status = 'failed'
iterationNode.error = item.error

View File

@ -169,8 +169,8 @@ type Shape = {
setShowTips: (showTips: string) => void
iterTimes: number
setIterTimes: (iterTimes: number) => void
iterParallelLogMap: Map<string, NodeTracing[]>
setIterParallelLogMap: (iterParallelLogMap: Map<string, NodeTracing[]>) => void
iterParallelLogMap: Map<string, Map<string, NodeTracing[]>>
setIterParallelLogMap: (iterParallelLogMap: Map<string, Map<string, NodeTracing[]>>) => void
}
export const createWorkflowStore = () => {
@ -288,7 +288,7 @@ export const createWorkflowStore = () => {
setShowTips: showTips => set(() => ({ showTips })),
iterTimes: 1,
setIterTimes: iterTimes => set(() => ({ iterTimes })),
iterParallelLogMap: new Map<string, NodeTracing[]>(),
iterParallelLogMap: new Map<string, Map<string, NodeTracing[]>>(),
setIterParallelLogMap: iterParallelLogMap => set(() => ({ iterParallelLogMap })),
}))

View File

@ -29,9 +29,17 @@ async function translateMissingKeyDeeply(sourceObj, targetObject, toLanguage) {
await translateMissingKeyDeeply(sourceObj[key], targetObject[key], toLanguage)
}
else {
const { translation } = await translate(sourceObj[key], null, languageKeyMap[toLanguage])
targetObject[key] = translation
// console.log(translation)
try {
if (!sourceObj[key]) {
targetObject[key] = ''
return
}
const { translation } = await translate(sourceObj[key], null, languageKeyMap[toLanguage])
targetObject[key] = translation
}
catch (e) {
console.error(`Error translating ${sourceObj[key]}(${key}) to ${toLanguage}`)
}
}
}
else if (typeof sourceObj[key] === 'object') {

View File

@ -595,6 +595,9 @@ const translation = {
expiring: 'Läuft an einem Tag ab',
expiring_plural: 'Läuft in {{count}} Tagen ab',
},
pagination: {
perPage: 'Artikel pro Seite',
},
}
export default translation

View File

@ -142,7 +142,7 @@ const translation = {
websiteSource: 'Preprocess-Website',
webpageUnit: 'Seiten',
separatorTip: 'Ein Trennzeichen ist das Zeichen, das zum Trennen von Text verwendet wird. \\n\\n und \\n sind häufig verwendete Trennzeichen zum Trennen von Absätzen und Zeilen. In Kombination mit Kommas (\\n\\n,\\n) werden Absätze nach Zeilen segmentiert, wenn die maximale Blocklänge überschritten wird. Sie können auch spezielle, von Ihnen selbst definierte Trennzeichen verwenden (z. B. ***).',
maxLengthCheck: 'Die maximale Stücklänge sollte weniger als 4000 betragen',
maxLengthCheck: 'Die maximale Stücklänge sollte weniger als {{limit}} betragen',
},
stepThree: {
creationTitle: '🎉 Wissen erstellt',

View File

@ -35,6 +35,8 @@ const translation = {
Translate: 'Übersetzen',
Programming: 'Programmieren',
HR: 'Personalwesen',
Agent: 'Agent',
Workflow: 'Arbeitsablauf',
},
}

View File

@ -500,6 +500,26 @@ const translation = {
'clear': 'Löschen',
'setVariable': 'Variable setzen',
'variable': 'Variable',
'operations': {
'title': 'Operation',
'clear': 'Klar',
'over-write': 'Überschreiben',
'set': 'Garnitur',
'-=': '-=',
'+=': '+=',
'/=': '/=',
'append': 'Anfügen',
'extend': 'Ausdehnen',
'*=': '*=',
'overwrite': 'Überschreiben',
},
'setParameter': 'Parameter setzen...',
'noVarTip': 'Klicken Sie auf die Schaltfläche "+", um Variablen hinzuzufügen',
'variables': 'Variablen',
'noAssignedVars': 'Keine verfügbaren zugewiesenen Variablen',
'selectAssignedVariable': 'Zugewiesene Variable auswählen...',
'varNotSet': 'Variable NICHT gesetzt',
'assignedVarsDescription': 'Zugewiesene Variablen müssen beschreibbare Variablen sein, z. B. Konversationsvariablen.',
},
tool: {
toAuthorize: 'Autorisieren',
@ -631,6 +651,13 @@ const translation = {
tracing: {
stopBy: 'Gestoppt von {{user}}',
},
variableReference: {
noAvailableVars: 'Keine verfügbaren Variablen',
conversationVars: 'Konversations-Variablen',
noAssignedVars: 'Keine verfügbaren zugewiesenen Variablen',
noVarsForOperation: 'Es stehen keine Variablen für die Zuweisung mit der ausgewählten Operation zur Verfügung.',
assignedVarsDescription: 'Zugewiesene Variablen müssen beschreibbare Variablen sein, z. B.',
},
}
export default translation

View File

@ -595,6 +595,9 @@ const translation = {
expiring: 'Expiring in one day',
expiring_plural: 'Expiring in {{count}} days',
},
pagination: {
perPage: 'Items per page',
},
}
export default translation

View File

@ -113,7 +113,7 @@ const translation = {
separatorTip: 'A delimiter is the character used to separate text. \\n\\n and \\n are commonly used delimiters for separating paragraphs and lines. Combined with commas (\\n\\n,\\n), paragraphs will be segmented by lines when exceeding the maximum chunk length. You can also use special delimiters defined by yourself (e.g. ***).',
separatorPlaceholder: '\\n\\n for separating paragraphs; \\n for separating lines',
maxLength: 'Maximum chunk length',
maxLengthCheck: 'Maximum chunk length should be less than 4000',
maxLengthCheck: 'Maximum chunk length should be less than {{limit}}',
overlap: 'Chunk overlap',
overlapTip: 'Setting the chunk overlap can maintain the semantic relevance between them, enhancing the retrieve effect. It is recommended to set 10%-25% of the maximum chunk size.',
overlapCheck: 'chunk overlap should not bigger than maximum chunk length',

View File

@ -30,11 +30,13 @@ const translation = {
nameRequired: 'App name is required',
},
category: {
Agent: 'Agent',
Assistant: 'Assistant',
Writing: 'Writing',
Translate: 'Translate',
Programming: 'Programming',
HR: 'HR',
Workflow: 'Workflow',
},
}

View File

@ -260,6 +260,13 @@ const translation = {
zoomTo100: 'Zoom to 100%',
zoomToFit: 'Zoom to Fit',
},
variableReference: {
noAvailableVars: 'No available variables',
noVarsForOperation: 'There are no variables available for assignment with the selected operation.',
noAssignedVars: 'No available assigned variables',
assignedVarsDescription: 'Assigned variables must be writable variables, such as ',
conversationVars: 'conversation variables',
},
panel: {
userInputField: 'User Input Field',
changeBlock: 'Change Block',
@ -491,6 +498,9 @@ const translation = {
},
assigner: {
'assignedVariable': 'Assigned Variable',
'varNotSet': 'Variable NOT Set',
'variables': 'Variables',
'noVarTip': 'Click the "+" button to add variables',
'writeMode': 'Write Mode',
'writeModeTip': 'Append mode: Available for array variables only.',
'over-write': 'Overwrite',
@ -498,7 +508,24 @@ const translation = {
'plus': 'Plus',
'clear': 'Clear',
'setVariable': 'Set Variable',
'selectAssignedVariable': 'Select assigned variable...',
'setParameter': 'Set parameter...',
'operations': {
'title': 'Operation',
'over-write': 'Overwrite',
'overwrite': 'Overwrite',
'set': 'Set',
'clear': 'Clear',
'extend': 'Extend',
'append': 'Append',
'+=': '+=',
'-=': '-=',
'*=': '*=',
'/=': '/=',
},
'variable': 'Variable',
'noAssignedVars': 'No available assigned variables',
'assignedVarsDescription': 'Assigned variables must be writable variables, such as conversation variables.',
},
tool: {
toAuthorize: 'To authorize',

View File

@ -595,6 +595,9 @@ const translation = {
expiring: 'Caduca en un día',
expiring_plural: 'Caducando en {{count}} días',
},
pagination: {
perPage: 'Elementos por página',
},
}
export default translation

View File

@ -147,7 +147,7 @@ const translation = {
retrievalSettingTip: 'Para cambiar el método de índice, por favor ve a la ',
datasetSettingLink: 'configuración del conocimiento.',
separatorTip: 'Un delimitador es el carácter que se utiliza para separar el texto. \\n\\n y \\n son delimitadores comúnmente utilizados para separar párrafos y líneas. Combinado con comas (\\n\\n,\\n), los párrafos se segmentarán por líneas cuando excedan la longitud máxima del fragmento. También puede utilizar delimitadores especiales definidos por usted mismo (por ejemplo, ***).',
maxLengthCheck: 'La longitud máxima del fragmento debe ser inferior a 4000',
maxLengthCheck: 'La longitud máxima del fragmento debe ser inferior a {{limit}}',
},
stepThree: {
creationTitle: '🎉 Conocimiento creado',

View File

@ -35,6 +35,8 @@ const translation = {
Translate: 'Traducción',
Programming: 'Programación',
HR: 'Recursos Humanos',
Agent: 'Agente',
Workflow: 'Flujo de trabajo',
},
}

View File

@ -500,6 +500,26 @@ const translation = {
'clear': 'Limpiar',
'setVariable': 'Establecer Variable',
'variable': 'Variable',
'operations': {
'clear': 'Claro',
'*=': '*=',
'-=': '-=',
'title': 'Operación',
'extend': 'Extender',
'append': 'Añadir',
'+=': '+=',
'over-write': 'Sobrescribir',
'overwrite': 'Sobrescribir',
'/=': '/=',
'set': 'Poner',
},
'variables': 'Variables',
'setParameter': 'Establecer parámetro...',
'noVarTip': 'Haga clic en el botón "+" para agregar variables',
'varNotSet': 'Variable NO establecida',
'noAssignedVars': 'No hay variables asignadas disponibles',
'selectAssignedVariable': 'Seleccione la variable asignada...',
'assignedVarsDescription': 'Las variables asignadas deben ser variables grabables, como las variables de conversación.',
},
tool: {
toAuthorize: 'Para autorizar',
@ -634,6 +654,13 @@ const translation = {
tracing: {
stopBy: 'Pásate por {{usuario}}',
},
variableReference: {
noAvailableVars: 'No hay variables disponibles',
assignedVarsDescription: 'Las variables asignadas deben ser variables grabables, como',
noVarsForOperation: 'No hay variables disponibles para la asignación con la operación seleccionada.',
noAssignedVars: 'No hay variables asignadas disponibles',
conversationVars: 'Variables de conversación',
},
}
export default translation

View File

@ -595,6 +595,9 @@ const translation = {
expiring_plural: 'انقضا در {{count}} روز',
expiring: 'انقضا در یک روز',
},
pagination: {
perPage: 'موارد در هر صفحه',
},
}
export default translation

View File

@ -147,7 +147,7 @@ const translation = {
retrievalSettingTip: 'برای تغییر روش شاخص، لطفاً به',
datasetSettingLink: 'تنظیمات دانش بروید.',
separatorTip: 'جداکننده نویسه ای است که برای جداسازی متن استفاده می شود. \\n\\n و \\n معمولا برای جداسازی پاراگراف ها و خطوط استفاده می شوند. همراه با کاما (\\n\\n,\\n)، پاراگراف ها زمانی که از حداکثر طول تکه فراتر می روند، با خطوط تقسیم بندی می شوند. همچنین می توانید از جداکننده های خاصی که توسط خودتان تعریف شده اند استفاده کنید (مثلا ***).',
maxLengthCheck: 'حداکثر طول تکه باید کمتر از 4000 باشد',
maxLengthCheck: 'حداکثر طول تکه باید کمتر از {{limit}} باشد',
},
stepThree: {
creationTitle: ' دانش ایجاد شد',

View File

@ -35,6 +35,8 @@ const translation = {
Translate: 'ترجمه',
Programming: 'برنامه‌نویسی',
HR: 'منابع انسانی',
Agent: 'عامل',
Workflow: 'گردش',
},
}

View File

@ -500,6 +500,26 @@ const translation = {
'clear': 'پاک کردن',
'setVariable': 'تنظیم متغیر',
'variable': 'متغیر',
'operations': {
'clear': 'روشن',
'over-write': 'بازنویسی',
'set': 'مجموعه',
'*=': '*=',
'overwrite': 'بازنویسی',
'+=': '+=',
'title': 'عملیات',
'extend': 'گسترش',
'-=': '-=',
'append': 'الحاق',
'/=': '/=',
},
'noVarTip': 'برای افزودن متغیرها روی دکمه "+" کلیک کنید',
'selectAssignedVariable': 'متغیر اختصاص داده شده را انتخاب کنید...',
'noAssignedVars': 'هیچ متغیر اختصاص داده شده در دسترس نیست',
'setParameter': 'پارامتر را تنظیم کنید...',
'assignedVarsDescription': 'متغیرهای اختصاص داده شده باید متغیرهای قابل نوشتن مانند متغیرهای مکالمه باشند.',
'variables': 'متغیرهای',
'varNotSet': 'متغیر NOT Set',
},
tool: {
toAuthorize: 'برای مجوز دادن',
@ -631,6 +651,13 @@ const translation = {
tracing: {
stopBy: 'متوقف شده توسط {{user}}',
},
variableReference: {
noAvailableVars: 'هیچ متغیری در دسترس نیست',
conversationVars: 'متغیرهای مکالمه',
noVarsForOperation: 'هیچ متغیری برای تخصیص با عملیات انتخاب شده در دسترس نیست.',
assignedVarsDescription: 'متغیرهای اختصاص داده شده باید متغیرهای قابل نوشتن باشند، مانند',
noAssignedVars: 'هیچ متغیر اختصاص داده شده در دسترس نیست',
},
}
export default translation

View File

@ -595,6 +595,9 @@ const translation = {
expiring: 'Expirant dans un jour',
expiring_plural: 'Expirant dans {{count}} jours',
},
pagination: {
perPage: 'Articles par page',
},
}
export default translation

View File

@ -142,7 +142,7 @@ const translation = {
webpageUnit: 'Pages',
websiteSource: 'Site web de prétraitement',
separatorTip: 'Un délimiteur est le caractère utilisé pour séparer le texte. \\n\\n et \\n sont des délimiteurs couramment utilisés pour séparer les paragraphes et les lignes. Combiné à des virgules (\\n\\n,\\n), les paragraphes seront segmentés par des lignes lorsquils dépasseront la longueur maximale des morceaux. Vous pouvez également utiliser des délimiteurs spéciaux définis par vous-même (par exemple ***).',
maxLengthCheck: 'La longueur maximale des morceaux doit être inférieure à 4000',
maxLengthCheck: 'La longueur maximale des morceaux doit être inférieure à {{limit}}',
},
stepThree: {
creationTitle: '🎉 Connaissance créée',

View File

@ -35,6 +35,8 @@ const translation = {
Translate: 'Traduire',
Programming: 'Programmation',
HR: 'RH',
Agent: 'Agent',
Workflow: 'Flux de travail',
},
}

View File

@ -500,6 +500,26 @@ const translation = {
'clear': 'Effacer',
'setVariable': 'Définir Variable',
'variable': 'Variable',
'operations': {
'clear': 'Clair',
'*=': '*=',
'-=': '-=',
'extend': 'Étendre',
'+=': '+=',
'over-write': 'Écraser',
'set': 'Poser',
'append': 'Ajouter',
'title': 'Opération',
'/=': '/=',
'overwrite': 'Écraser',
},
'assignedVarsDescription': 'Les variables affectées doivent être accessibles en écriture, telles que des variables de conversation.',
'noVarTip': 'Cliquez sur le bouton « + » pour ajouter des variables',
'variables': 'Variables',
'setParameter': 'Définir le paramètre...',
'noAssignedVars': 'Aucune variable affectée disponible',
'varNotSet': 'Variable NON définie',
'selectAssignedVariable': 'Sélectionner la variable affectée...',
},
tool: {
toAuthorize: 'Autoriser',
@ -631,6 +651,13 @@ const translation = {
tracing: {
stopBy: 'Arrêté par {{user}}',
},
variableReference: {
noAssignedVars: 'Aucune variable affectée disponible',
noVarsForOperation: 'Aucune variable nest disponible pour laffectation avec lopération sélectionnée.',
noAvailableVars: 'Aucune variable disponible',
assignedVarsDescription: 'Les variables affectées doivent être des variables accessibles en écriture, telles que',
conversationVars: 'Variables de conversation',
},
}
export default translation

View File

@ -617,6 +617,9 @@ const translation = {
expiring: 'एक दिन में समाप्त हो रहा है',
expiring_plural: '{{गिनती}} दिनों में समाप्त हो रहा है',
},
pagination: {
perPage: 'प्रति पृष्ठ आइटम',
},
}
export default translation

View File

@ -164,7 +164,7 @@ const translation = {
retrievalSettingTip: 'इंडेक्स विधि बदलने के लिए, कृपया जाएं ',
datasetSettingLink: 'ज्ञान सेटिंग्स।',
separatorTip: 'एक सीमांकक पाठ को अलग करने के लिए उपयोग किया जाने वाला वर्ण है। \\n\\n और \\n आमतौर पर पैराग्राफ और लाइनों को अलग करने के लिए उपयोग किए जाने वाले सीमांकक हैं। अल्पविराम (\\n\\n,\\n) के साथ संयुक्त, अधिकतम खंड लंबाई से अधिक होने पर अनुच्छेदों को पंक्तियों द्वारा खंडित किया जाएगा। आप स्वयं द्वारा परिभाषित विशेष सीमांकक का भी उपयोग कर सकते हैं (उदा. ***).',
maxLengthCheck: 'अधिकतम चंक लंबाई 4000 से कम होनी चाहिए',
maxLengthCheck: 'अधिकतम चंक लंबाई {{limit}} से कम होनी चाहिए',
},
stepThree: {
creationTitle: '🎉 ज्ञान बनाया गया',

View File

@ -36,6 +36,8 @@ const translation = {
Translate: 'अनुवाद',
Programming: 'प्रोग्रामिंग',
HR: 'मानव संसाधन',
Workflow: 'कार्यप्रवाह',
Agent: 'आढ़तिया',
},
}

View File

@ -516,6 +516,26 @@ const translation = {
'clear': 'साफ़ करें',
'setVariable': 'चर सेट करें',
'variable': 'चर',
'operations': {
'clear': 'स्पष्ट',
'/=': '/=',
'*=': '*=',
'over-write': 'अधिलेखित',
'title': 'परिचालन',
'+=': '+=',
'overwrite': 'अधिलेखित',
'set': 'अस्त हो',
'extend': 'पसार',
'-=': '-=',
'append': 'संलग्न',
},
'setParameter': 'पैरामीटर सेट करें...',
'noVarTip': 'चर जोड़ने के लिए "+" बटन पर क्लिक करें',
'variables': 'चर',
'selectAssignedVariable': 'असाइन किए गए चर का चयन करें...',
'varNotSet': 'चर सेट नहीं',
'assignedVarsDescription': 'असाइन किए गए चर लिखने योग्य चर होने चाहिए, जैसे वार्तालाप चर।',
'noAssignedVars': 'कोई उपलब्ध असाइन किए गए चर नहीं',
},
tool: {
toAuthorize: 'अधिकृत करने के लिए',
@ -651,6 +671,13 @@ const translation = {
tracing: {
stopBy: '{{user}} द्वारा रोका गया',
},
variableReference: {
conversationVars: 'बातचीत चर',
noAvailableVars: 'कोई उपलब्ध चर नहीं',
assignedVarsDescription: 'असाइन किए गए चर लिखने योग्य चर होने चाहिए, जैसे',
noVarsForOperation: 'चयनित कार्रवाई के साथ असाइनमेंट के लिए कोई चर उपलब्ध नहीं हैं.',
noAssignedVars: 'कोई उपलब्ध असाइन किए गए चर नहीं',
},
}
export default translation

View File

@ -626,6 +626,9 @@ const translation = {
expiring_plural: 'Scadenza tra {{count}} giorni',
expiring: 'Scadenza in un giorno',
},
pagination: {
perPage: 'Articoli per pagina',
},
}
export default translation

View File

@ -167,7 +167,7 @@ const translation = {
retrievalSettingTip: 'Per cambiare il metodo di indicizzazione, vai alle ',
datasetSettingLink: 'impostazioni della Conoscenza.',
separatorTip: 'Un delimitatore è il carattere utilizzato per separare il testo. \\n\\n e \\n sono delimitatori comunemente usati per separare paragrafi e righe. In combinazione con le virgole (\\n\\n,\\n), i paragrafi verranno segmentati per righe quando superano la lunghezza massima del blocco. È inoltre possibile utilizzare delimitatori speciali definiti dall\'utente (ad es. ***).',
maxLengthCheck: 'La lunghezza massima del blocco deve essere inferiore a 4000',
maxLengthCheck: 'La lunghezza massima del blocco deve essere inferiore a {{limit}}',
},
stepThree: {
creationTitle: '🎉 Conoscenza creata',

View File

@ -36,6 +36,8 @@ const translation = {
Translate: 'Traduzione',
Programming: 'Programmazione',
HR: 'Risorse Umane',
Workflow: 'Flusso di lavoro',
Agent: 'Agente',
},
}

View File

@ -520,6 +520,26 @@ const translation = {
'clear': 'Cancellare',
'setVariable': 'Imposta Variabile',
'variable': 'Variabile',
'operations': {
'-=': '-=',
'overwrite': 'Sovrascrivere',
'+=': '+=',
'*=': '*=',
'append': 'Aggiungere',
'set': 'Mettere',
'title': 'Operazione',
'/=': '/=',
'over-write': 'Sovrascrivere',
'extend': 'Estendere',
'clear': 'Chiaro',
},
'setParameter': 'Imposta parametro...',
'variables': 'Variabili',
'noAssignedVars': 'Nessuna variabile assegnata disponibile',
'assignedVarsDescription': 'Le variabili assegnate devono essere variabili scrivibili, ad esempio variabili di conversazione.',
'varNotSet': 'Variabile NON impostata',
'selectAssignedVariable': 'Seleziona variabile assegnata...',
'noVarTip': 'Fare clic sul pulsante "+" per aggiungere variabili',
},
tool: {
toAuthorize: 'Per autorizzare',
@ -658,6 +678,13 @@ const translation = {
tracing: {
stopBy: 'Interrotto da {{user}}',
},
variableReference: {
noAvailableVars: 'Nessuna variabile disponibile',
noAssignedVars: 'Nessuna variabile assegnata disponibile',
noVarsForOperation: 'Non ci sono variabili disponibili per l\'assegnazione con l\'operazione selezionata.',
assignedVarsDescription: 'Le variabili assegnate devono essere variabili scrivibili, ad esempio',
conversationVars: 'Variabili di conversazione',
},
}
export default translation

View File

@ -595,6 +595,9 @@ const translation = {
expiring_plural: '有効期限 {{count}} 日',
expiring: '1日で有効期限が切れます',
},
pagination: {
perPage: 'ページあたりのアイテム数',
},
}
export default translation

View File

@ -147,7 +147,7 @@ const translation = {
retrievalSettingTip: '検索方法を変更するには、',
datasetSettingLink: 'ナレッジ設定',
separatorTip: '区切り文字は、テキストを区切るために使用される文字です。\\n\\n と \\n は、段落と行を区切るために一般的に使用される区切り記号です。カンマ (\\n\\n,\\n) と組み合わせると、最大チャンク長を超えると、段落は行で区切られます。自分で定義した特別な区切り文字を使用することもできます(例:***)。',
maxLengthCheck: 'チャンクの最大長は 4000 未満にする必要があります',
maxLengthCheck: 'チャンクの最大長は {{limit}} 未満にする必要があります',
},
stepThree: {
creationTitle: '🎉 ナレッジが作成されました',

View File

@ -35,6 +35,8 @@ const translation = {
Translate: '翻訳',
Programming: 'プログラミング',
HR: '人事',
Workflow: 'ワークフロー',
Agent: 'エージェント',
},
}

Some files were not shown because too many files have changed in this diff Show More