mirror of
https://github.com/langgenius/dify.git
synced 2026-05-05 18:08:07 +08:00
Merge branch 'main' into feat/parent-child-retrieval
This commit is contained in:
@ -12,34 +12,46 @@ import ConfigContext from '@/context/debug-configuration'
|
||||
// import { Resolution } from '@/types/app'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import type { FileUpload } from '@/app/components/base/features/types'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
|
||||
const ConfigVision: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { isShowVisionConfig } = useContext(ConfigContext)
|
||||
const { isShowVisionConfig, isAllowVideoUpload } = useContext(ConfigContext)
|
||||
const file = useFeatures(s => s.features.file)
|
||||
const featuresStore = useFeaturesStore()
|
||||
|
||||
const handleChange = useCallback((data: FileUpload) => {
|
||||
const isImageEnabled = file?.allowed_file_types?.includes(SupportUploadFileTypes.image) ?? false
|
||||
|
||||
const handleChange = useCallback((value: boolean) => {
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = featuresStore!.getState()
|
||||
|
||||
const newFeatures = produce(features, (draft) => {
|
||||
draft.file = {
|
||||
...draft.file,
|
||||
enabled: data.enabled,
|
||||
image: {
|
||||
enabled: data.enabled,
|
||||
detail: data.image?.detail,
|
||||
transfer_methods: data.image?.transfer_methods,
|
||||
number_limits: data.image?.number_limits,
|
||||
},
|
||||
if (value) {
|
||||
draft.file!.allowed_file_types = Array.from(new Set([
|
||||
...(draft.file?.allowed_file_types || []),
|
||||
SupportUploadFileTypes.image,
|
||||
...(isAllowVideoUpload ? [SupportUploadFileTypes.video] : []),
|
||||
]))
|
||||
}
|
||||
else {
|
||||
draft.file!.allowed_file_types = draft.file!.allowed_file_types?.filter(
|
||||
type => type !== SupportUploadFileTypes.image && (isAllowVideoUpload ? type !== SupportUploadFileTypes.video : true),
|
||||
)
|
||||
}
|
||||
|
||||
if (draft.file) {
|
||||
draft.file.enabled = (draft.file.allowed_file_types?.length ?? 0) > 0
|
||||
draft.file.image = {
|
||||
...(draft.file.image || {}),
|
||||
enabled: value,
|
||||
}
|
||||
}
|
||||
})
|
||||
setFeatures(newFeatures)
|
||||
}, [featuresStore])
|
||||
}, [featuresStore, isAllowVideoUpload])
|
||||
|
||||
if (!isShowVisionConfig)
|
||||
return null
|
||||
@ -89,11 +101,8 @@ const ConfigVision: FC = () => {
|
||||
<ParamConfig />
|
||||
<div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-subtle'></div>
|
||||
<Switch
|
||||
defaultValue={file?.enabled}
|
||||
onChange={value => handleChange({
|
||||
...(file || {}),
|
||||
enabled: value,
|
||||
})}
|
||||
defaultValue={isImageEnabled}
|
||||
onChange={handleChange}
|
||||
size='md'
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -0,0 +1,78 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import produce from 'immer'
|
||||
import { useContext } from 'use-context-selector'
|
||||
|
||||
import { Document } from '@/app/components/base/icons/src/vender/features'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
||||
import { useFeatures, useFeaturesStore } from '@/app/components/base/features/hooks'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
|
||||
const ConfigDocument: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const file = useFeatures(s => s.features.file)
|
||||
const featuresStore = useFeaturesStore()
|
||||
const { isShowDocumentConfig } = useContext(ConfigContext)
|
||||
|
||||
const isDocumentEnabled = file?.allowed_file_types?.includes(SupportUploadFileTypes.document) ?? false
|
||||
|
||||
const handleChange = useCallback((value: boolean) => {
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = featuresStore!.getState()
|
||||
|
||||
const newFeatures = produce(features, (draft) => {
|
||||
if (value) {
|
||||
draft.file!.allowed_file_types = Array.from(new Set([
|
||||
...(draft.file?.allowed_file_types || []),
|
||||
SupportUploadFileTypes.document,
|
||||
]))
|
||||
}
|
||||
else {
|
||||
draft.file!.allowed_file_types = draft.file!.allowed_file_types?.filter(
|
||||
type => type !== SupportUploadFileTypes.document,
|
||||
)
|
||||
}
|
||||
if (draft.file)
|
||||
draft.file.enabled = (draft.file.allowed_file_types?.length ?? 0) > 0
|
||||
})
|
||||
setFeatures(newFeatures)
|
||||
}, [featuresStore])
|
||||
|
||||
if (!isShowDocumentConfig)
|
||||
return null
|
||||
|
||||
return (
|
||||
<div className='mt-2 flex items-center gap-2 p-2 rounded-xl border-t-[0.5px] border-l-[0.5px] bg-background-section-burn'>
|
||||
<div className='shrink-0 p-1'>
|
||||
<div className='p-1 rounded-lg border-[0.5px] border-divider-subtle shadow-xs bg-util-colors-indigo-indigo-600'>
|
||||
<Document className='w-4 h-4 text-text-primary-on-surface' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='grow flex items-center'>
|
||||
<div className='mr-1 text-text-secondary system-sm-semibold'>{t('appDebug.feature.documentUpload.title')}</div>
|
||||
<Tooltip
|
||||
popupContent={
|
||||
<div className='w-[180px]' >
|
||||
{t('appDebug.feature.documentUpload.description')}
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className='shrink-0 flex items-center'>
|
||||
<div className='ml-1 mr-3 w-[1px] h-3.5 bg-divider-subtle'></div>
|
||||
<Switch
|
||||
defaultValue={isDocumentEnabled}
|
||||
onChange={handleChange}
|
||||
size='md'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default React.memo(ConfigDocument)
|
||||
@ -7,6 +7,7 @@ import { useFormattingChangedDispatcher } from '../debug/hooks'
|
||||
import DatasetConfig from '../dataset-config'
|
||||
import HistoryPanel from '../config-prompt/conversation-history/history-panel'
|
||||
import ConfigVision from '../config-vision'
|
||||
import ConfigDocument from './config-document'
|
||||
import AgentTools from './agent/agent-tools'
|
||||
import ConfigContext from '@/context/debug-configuration'
|
||||
import ConfigPrompt from '@/app/components/app/configuration/config-prompt'
|
||||
@ -82,6 +83,8 @@ const Config: FC = () => {
|
||||
|
||||
<ConfigVision />
|
||||
|
||||
<ConfigDocument />
|
||||
|
||||
{/* Chat History */}
|
||||
{isAdvancedMode && isChatApp && modelModeType === ModelModeType.completion && (
|
||||
<HistoryPanel
|
||||
|
||||
@ -451,7 +451,8 @@ const Configuration: FC = () => {
|
||||
}
|
||||
|
||||
const isShowVisionConfig = !!currModel?.features?.includes(ModelFeatureEnum.vision)
|
||||
|
||||
const isShowDocumentConfig = !!currModel?.features?.includes(ModelFeatureEnum.document)
|
||||
const isAllowVideoUpload = !!currModel?.features?.includes(ModelFeatureEnum.video)
|
||||
// *** web app features ***
|
||||
const featuresData: FeaturesData = useMemo(() => {
|
||||
return {
|
||||
@ -472,7 +473,7 @@ const Configuration: FC = () => {
|
||||
transfer_methods: modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
},
|
||||
enabled: !!(modelConfig.file_upload?.enabled || modelConfig.file_upload?.image?.enabled),
|
||||
allowed_file_types: modelConfig.file_upload?.allowed_file_types || [SupportUploadFileTypes.image, SupportUploadFileTypes.video],
|
||||
allowed_file_types: modelConfig.file_upload?.allowed_file_types || [],
|
||||
allowed_file_extensions: modelConfig.file_upload?.allowed_file_extensions || [...FILE_EXTS[SupportUploadFileTypes.image], ...FILE_EXTS[SupportUploadFileTypes.video]].map(ext => `.${ext}`),
|
||||
allowed_file_upload_methods: modelConfig.file_upload?.allowed_file_upload_methods || modelConfig.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
number_limits: modelConfig.file_upload?.number_limits || modelConfig.file_upload?.image?.number_limits || 3,
|
||||
@ -861,6 +862,8 @@ const Configuration: FC = () => {
|
||||
isShowVisionConfig,
|
||||
visionConfig,
|
||||
setVisionConfig: handleSetVisionConfig,
|
||||
isAllowVideoUpload,
|
||||
isShowDocumentConfig,
|
||||
rerankSettingModalOpen,
|
||||
setRerankSettingModalOpen,
|
||||
}}
|
||||
|
||||
@ -12,9 +12,13 @@ import Input from '@/app/components/base/input'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import {
|
||||
importApp,
|
||||
importAppFromUrl,
|
||||
importDSL,
|
||||
importDSLConfirm,
|
||||
} from '@/service/apps'
|
||||
import {
|
||||
DSLImportMode,
|
||||
DSLImportStatus,
|
||||
} from '@/models/app'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { useProviderContext } from '@/context/provider-context'
|
||||
import AppsFull from '@/app/components/billing/apps-full-in-dialog'
|
||||
@ -43,6 +47,9 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
const [fileContent, setFileContent] = useState<string>()
|
||||
const [currentTab, setCurrentTab] = useState(activeTab)
|
||||
const [dslUrlValue, setDslUrlValue] = useState(dslUrl)
|
||||
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||
const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()
|
||||
const [importId, setImportId] = useState<string>()
|
||||
|
||||
const readFile = (file: File) => {
|
||||
const reader = new FileReader()
|
||||
@ -66,6 +73,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
const isAppsFull = (enableBilling && plan.usage.buildApps >= plan.total.buildApps)
|
||||
|
||||
const isCreatingRef = useRef(false)
|
||||
|
||||
const onCreate: MouseEventHandler = async () => {
|
||||
if (currentTab === CreateFromDSLModalTab.FROM_FILE && !currentFile)
|
||||
return
|
||||
@ -75,25 +83,54 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
return
|
||||
isCreatingRef.current = true
|
||||
try {
|
||||
let app
|
||||
let response
|
||||
|
||||
if (currentTab === CreateFromDSLModalTab.FROM_FILE) {
|
||||
app = await importApp({
|
||||
data: fileContent || '',
|
||||
response = await importDSL({
|
||||
mode: DSLImportMode.YAML_CONTENT,
|
||||
yaml_content: fileContent || '',
|
||||
})
|
||||
}
|
||||
if (currentTab === CreateFromDSLModalTab.FROM_URL) {
|
||||
app = await importAppFromUrl({
|
||||
url: dslUrlValue || '',
|
||||
response = await importDSL({
|
||||
mode: DSLImportMode.YAML_URL,
|
||||
yaml_url: dslUrlValue || '',
|
||||
})
|
||||
}
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
if (onClose)
|
||||
onClose()
|
||||
notify({ type: 'success', message: t('app.newApp.appCreated') })
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
getRedirection(isCurrentWorkspaceEditor, app, push)
|
||||
|
||||
if (!response)
|
||||
return
|
||||
|
||||
const { id, status, app_id, imported_dsl_version, current_dsl_version } = response
|
||||
if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
if (onClose)
|
||||
onClose()
|
||||
|
||||
notify({
|
||||
type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning',
|
||||
message: t(status === DSLImportStatus.COMPLETED ? 'app.newApp.appCreated' : 'app.newApp.caution'),
|
||||
children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('app.newApp.appCreateDSLWarning'),
|
||||
})
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
|
||||
}
|
||||
else if (status === DSLImportStatus.PENDING) {
|
||||
setVersions({
|
||||
importedVersion: imported_dsl_version ?? '',
|
||||
systemVersion: current_dsl_version ?? '',
|
||||
})
|
||||
if (onClose)
|
||||
onClose()
|
||||
setTimeout(() => {
|
||||
setShowErrorModal(true)
|
||||
}, 300)
|
||||
setImportId(id)
|
||||
}
|
||||
else {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
@ -101,6 +138,38 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
isCreatingRef.current = false
|
||||
}
|
||||
|
||||
const onDSLConfirm: MouseEventHandler = async () => {
|
||||
try {
|
||||
if (!importId)
|
||||
return
|
||||
const response = await importDSLConfirm({
|
||||
import_id: importId,
|
||||
})
|
||||
|
||||
const { status, app_id } = response
|
||||
|
||||
if (status === DSLImportStatus.COMPLETED) {
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
if (onClose)
|
||||
onClose()
|
||||
|
||||
notify({
|
||||
type: 'success',
|
||||
message: t('app.newApp.appCreated'),
|
||||
})
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
getRedirection(isCurrentWorkspaceEditor, { id: app_id }, push)
|
||||
}
|
||||
else if (status === DSLImportStatus.FAILED) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
}
|
||||
}
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
key: CreateFromDSLModalTab.FROM_FILE,
|
||||
@ -123,74 +192,96 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS
|
||||
}, [isAppsFull, currentTab, currentFile, dslUrlValue])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className='p-0 w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
|
||||
isShow={show}
|
||||
onClose={() => { }}
|
||||
>
|
||||
<div className='flex items-center justify-between pt-6 pl-6 pr-5 pb-3 text-text-primary title-2xl-semi-bold'>
|
||||
{t('app.importFromDSL')}
|
||||
<div
|
||||
className='flex items-center w-8 h-8 cursor-pointer'
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<RiCloseLine className='w-5 h-5 text-text-tertiary' />
|
||||
<>
|
||||
<Modal
|
||||
className='p-0 w-[520px] rounded-2xl border-[0.5px] border-components-panel-border bg-components-panel-bg shadow-xl'
|
||||
isShow={show}
|
||||
onClose={() => { }}
|
||||
>
|
||||
<div className='flex items-center justify-between pt-6 pl-6 pr-5 pb-3 text-text-primary title-2xl-semi-bold'>
|
||||
{t('app.importFromDSL')}
|
||||
<div
|
||||
className='flex items-center w-8 h-8 cursor-pointer'
|
||||
onClick={() => onClose()}
|
||||
>
|
||||
<RiCloseLine className='w-5 h-5 text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center px-6 h-9 space-x-6 system-md-semibold text-text-tertiary border-b border-divider-subtle'>
|
||||
{
|
||||
tabs.map(tab => (
|
||||
<div
|
||||
key={tab.key}
|
||||
className={cn(
|
||||
'relative flex items-center h-full cursor-pointer',
|
||||
currentTab === tab.key && 'text-text-primary',
|
||||
)}
|
||||
onClick={() => setCurrentTab(tab.key)}
|
||||
>
|
||||
{tab.label}
|
||||
{
|
||||
currentTab === tab.key && (
|
||||
<div className='absolute bottom-0 w-full h-[2px] bg-util-colors-blue-brand-blue-brand-600'></div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className='px-6 py-4'>
|
||||
{
|
||||
currentTab === CreateFromDSLModalTab.FROM_FILE && (
|
||||
<Uploader
|
||||
className='mt-0'
|
||||
file={currentFile}
|
||||
updateFile={handleFile}
|
||||
/>
|
||||
)
|
||||
}
|
||||
{
|
||||
currentTab === CreateFromDSLModalTab.FROM_URL && (
|
||||
<div>
|
||||
<div className='mb-1 system-md-semibold leading6'>DSL URL</div>
|
||||
<Input
|
||||
placeholder={t('app.importFromDSLUrlPlaceholder') || ''}
|
||||
value={dslUrlValue}
|
||||
onChange={e => setDslUrlValue(e.target.value)}
|
||||
<div className='flex items-center px-6 h-9 space-x-6 system-md-semibold text-text-tertiary border-b border-divider-subtle'>
|
||||
{
|
||||
tabs.map(tab => (
|
||||
<div
|
||||
key={tab.key}
|
||||
className={cn(
|
||||
'relative flex items-center h-full cursor-pointer',
|
||||
currentTab === tab.key && 'text-text-primary',
|
||||
)}
|
||||
onClick={() => setCurrentTab(tab.key)}
|
||||
>
|
||||
{tab.label}
|
||||
{
|
||||
currentTab === tab.key && (
|
||||
<div className='absolute bottom-0 w-full h-[2px] bg-util-colors-blue-brand-blue-brand-600'></div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className='px-6 py-4'>
|
||||
{
|
||||
currentTab === CreateFromDSLModalTab.FROM_FILE && (
|
||||
<Uploader
|
||||
className='mt-0'
|
||||
file={currentFile}
|
||||
updateFile={handleFile}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
{isAppsFull && (
|
||||
<div className='px-6'>
|
||||
<AppsFull className='mt-0' loc='app-create-dsl' />
|
||||
)
|
||||
}
|
||||
{
|
||||
currentTab === CreateFromDSLModalTab.FROM_URL && (
|
||||
<div>
|
||||
<div className='mb-1 system-md-semibold leading6'>DSL URL</div>
|
||||
<Input
|
||||
placeholder={t('app.importFromDSLUrlPlaceholder') || ''}
|
||||
value={dslUrlValue}
|
||||
onChange={e => setDslUrlValue(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
<div className='flex justify-end px-6 py-5'>
|
||||
<Button className='mr-2' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button disabled={buttonDisabled} variant="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
{isAppsFull && (
|
||||
<div className='px-6'>
|
||||
<AppsFull className='mt-0' loc='app-create-dsl' />
|
||||
</div>
|
||||
)}
|
||||
<div className='flex justify-end px-6 py-5'>
|
||||
<Button className='mr-2' onClick={onClose}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button disabled={buttonDisabled} variant="primary" onClick={onCreate}>{t('app.newApp.Create')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<Modal
|
||||
isShow={showErrorModal}
|
||||
onClose={() => setShowErrorModal(false)}
|
||||
className='w-[480px]'
|
||||
>
|
||||
<div className='flex pb-4 flex-col items-start gap-2 self-stretch'>
|
||||
<div className='text-text-primary title-2xl-semi-bold'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
|
||||
<div className='flex flex-grow flex-col text-text-secondary system-md-regular'>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart1')}</div>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart2')}</div>
|
||||
<br />
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart3')}<span className='system-md-medium'>{versions?.importedVersion}</span></div>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart4')}<span className='system-md-medium'>{versions?.systemVersion}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex pt-6 justify-end items-start gap-2 self-stretch'>
|
||||
<Button variant='secondary' onClick={() => setShowErrorModal(false)}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button variant='primary' destructive onClick={onDSLConfirm}>{t('app.newApp.Confirm')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
} from '@remixicon/react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { formatFileSize } from '@/utils/format'
|
||||
import cn from '@/utils/classnames'
|
||||
import { Yaml as YamlIcon } from '@/app/components/base/icons/src/public/files'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
@ -58,8 +59,13 @@ const Uploader: FC<Props> = ({
|
||||
updateFile(files[0])
|
||||
}
|
||||
const selectHandle = () => {
|
||||
if (fileUploader.current)
|
||||
const originalFile = file
|
||||
if (fileUploader.current) {
|
||||
fileUploader.current.value = ''
|
||||
fileUploader.current.click()
|
||||
// If no file is selected, restore the original file
|
||||
fileUploader.current.oncancel = () => updateFile(originalFile)
|
||||
}
|
||||
}
|
||||
const removeFile = () => {
|
||||
if (fileUploader.current)
|
||||
@ -96,7 +102,7 @@ const Uploader: FC<Props> = ({
|
||||
/>
|
||||
<div ref={dropRef}>
|
||||
{!file && (
|
||||
<div className={cn('flex items-center h-20 rounded-xl bg-gray-50 border border-dashed border-gray-200 text-sm font-normal', dragging && 'bg-[#F5F8FF] border border-[#B2CCFF]')}>
|
||||
<div className={cn('flex items-center h-12 rounded-xl bg-gray-50 border border-dashed border-gray-200 text-sm font-normal', dragging && 'bg-[#F5F8FF] border border-[#B2CCFF]')}>
|
||||
<div className='w-full flex items-center justify-center space-x-2'>
|
||||
<UploadCloud01 className='w-6 h-6 mr-2' />
|
||||
<div className='text-gray-500'>
|
||||
@ -108,17 +114,23 @@ const Uploader: FC<Props> = ({
|
||||
</div>
|
||||
)}
|
||||
{file && (
|
||||
<div className={cn('flex items-center h-20 px-6 rounded-xl bg-gray-50 border border-gray-200 text-sm font-normal group', 'hover:bg-[#F5F8FF] hover:border-[#B2CCFF]')}>
|
||||
<YamlIcon className="shrink-0" />
|
||||
<div className='flex ml-2 w-0 grow'>
|
||||
<span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-gray-800'>{file.name.replace(/(.yaml|.yml)$/, '')}</span>
|
||||
<span className='shrink-0 text-gray-500'>.yml</span>
|
||||
<div className={cn('flex items-center rounded-lg bg-components-panel-on-panel-item-bg border-[0.5px] border-components-panel-border shadow-xs group', 'hover:bg-[#F5F8FF] hover:border-[#B2CCFF]')}>
|
||||
<div className='flex p-3 justify-center items-center'>
|
||||
<YamlIcon className="w-6 h-6 shrink-0" />
|
||||
</div>
|
||||
<div className='flex py-1 pr-2 grow flex-col items-start gap-0.5'>
|
||||
<span className='max-w-[calc(100%_-_30px)] text-ellipsis whitespace-nowrap overflow-hidden text-text-secondary font-inter text-[12px] font-medium leading-4'>{file.name}</span>
|
||||
<div className='flex h-3 items-center gap-1 self-stretch text-text-tertiary font-inter text-[10px] font-medium leading-3 uppercase'>
|
||||
<span>YAML</span>
|
||||
<span className='text-text-quaternary'>·</span>
|
||||
<span>{formatFileSize(file.size)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='hidden group-hover:flex items-center'>
|
||||
<Button onClick={selectHandle}>{t('datasetCreation.stepOne.uploader.change')}</Button>
|
||||
<div className='mx-2 w-px h-4 bg-gray-200' />
|
||||
<div className='p-2 cursor-pointer' onClick={removeFile}>
|
||||
<RiDeleteBinLine className='w-4 h-4 text-gray-500' />
|
||||
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1804,8 +1804,85 @@ exports[`build chat item tree and get thread messages should get thread messages
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with partial messages 1`] = `
|
||||
exports[`build chat item tree and get thread messages should work with partial messages 1 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"agent_thoughts": [
|
||||
{
|
||||
"chain_id": null,
|
||||
"created_at": 1726105799,
|
||||
"files": [],
|
||||
"id": "9730d587-9268-4683-9dd9-91a1cab9510b",
|
||||
"message_id": "4c5d0841-1206-463e-95d8-71f812877658",
|
||||
"observation": "",
|
||||
"position": 1,
|
||||
"thought": "I'll go with 112. Your turn!",
|
||||
"tool": "",
|
||||
"tool_input": "",
|
||||
"tool_labels": {},
|
||||
},
|
||||
],
|
||||
"children": [],
|
||||
"content": "I'll go with 112. Your turn!",
|
||||
"conversationId": "dd6c9cfd-2656-48ec-bd51-2139c1790d80",
|
||||
"feedbackDisabled": false,
|
||||
"id": "4c5d0841-1206-463e-95d8-71f812877658",
|
||||
"input": {
|
||||
"inputs": {},
|
||||
"query": "99",
|
||||
},
|
||||
"isAnswer": true,
|
||||
"log": [
|
||||
{
|
||||
"files": [],
|
||||
"role": "user",
|
||||
"text": "Let's play a game, I say a number , and you response me with another bigger, yet randomly number. I'll start first, 38",
|
||||
},
|
||||
{
|
||||
"files": [],
|
||||
"role": "assistant",
|
||||
"text": "Sure, I'll play! My number is 57. Your turn!",
|
||||
},
|
||||
{
|
||||
"files": [],
|
||||
"role": "user",
|
||||
"text": "58",
|
||||
},
|
||||
{
|
||||
"files": [],
|
||||
"role": "assistant",
|
||||
"text": "I choose 83. What's your next number?",
|
||||
},
|
||||
{
|
||||
"files": [],
|
||||
"role": "user",
|
||||
"text": "99",
|
||||
},
|
||||
{
|
||||
"files": [],
|
||||
"role": "assistant",
|
||||
"text": "I'll go with 112. Your turn!",
|
||||
},
|
||||
],
|
||||
"message_files": [],
|
||||
"more": {
|
||||
"latency": "1.49",
|
||||
"time": "09/11/2024 09:50 PM",
|
||||
"tokens": 86,
|
||||
},
|
||||
"parentMessageId": "question-4c5d0841-1206-463e-95d8-71f812877658",
|
||||
"siblingIndex": 0,
|
||||
"workflow_run_id": null,
|
||||
},
|
||||
],
|
||||
"content": "99",
|
||||
"id": "question-4c5d0841-1206-463e-95d8-71f812877658",
|
||||
"isAnswer": false,
|
||||
"message_files": [],
|
||||
"parentMessageId": "73bbad14-d915-499d-87bf-0df14d40779d",
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
@ -2078,6 +2155,178 @@ exports[`build chat item tree and get thread messages should work with partial m
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with partial messages 2 1`] = `
|
||||
[
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"content": "237.",
|
||||
"id": "ebb73fe2-15de-46dd-aab5-75416d8448eb",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-ebb73fe2-15de-46dd-aab5-75416d8448eb",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "123",
|
||||
"id": "question-ebb73fe2-15de-46dd-aab5-75416d8448eb",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418",
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"content": "My number is 256.",
|
||||
"id": "3553d508-3850-462e-8594-078539f940f9",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-3553d508-3850-462e-8594-078539f940f9",
|
||||
"siblingIndex": 1,
|
||||
},
|
||||
],
|
||||
"content": "123",
|
||||
"id": "question-3553d508-3850-462e-8594-078539f940f9",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418",
|
||||
},
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [
|
||||
{
|
||||
"children": [],
|
||||
"content": "My number is 3e (approximately 8.15).",
|
||||
"id": "9eac3bcc-8d3b-4e56-a12b-44c34cebc719",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-9eac3bcc-8d3b-4e56-a12b-44c34cebc719",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "e",
|
||||
"id": "question-9eac3bcc-8d3b-4e56-a12b-44c34cebc719",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
},
|
||||
],
|
||||
"content": "My number is 2π (approximately 6.28).",
|
||||
"id": "5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "π",
|
||||
"id": "question-5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
},
|
||||
],
|
||||
"content": "My number is 145.",
|
||||
"id": "46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "78",
|
||||
"id": "question-46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
},
|
||||
],
|
||||
"content": "My number is 7.89.",
|
||||
"id": "3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "3.11",
|
||||
"id": "question-3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
},
|
||||
],
|
||||
"content": "My number is 22.",
|
||||
"id": "a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "-5",
|
||||
"id": "question-a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
},
|
||||
],
|
||||
"content": "My number is 4782.",
|
||||
"id": "93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "3306",
|
||||
"id": "question-93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
},
|
||||
],
|
||||
"content": "My number is 2048.",
|
||||
"id": "9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
"siblingIndex": 0,
|
||||
},
|
||||
],
|
||||
"content": "1024",
|
||||
"id": "question-9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
},
|
||||
],
|
||||
"content": "My number is 259.",
|
||||
"id": "507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
"siblingIndex": 2,
|
||||
},
|
||||
],
|
||||
"content": "123",
|
||||
"id": "question-507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`build chat item tree and get thread messages should work with real world messages 1`] = `
|
||||
[
|
||||
{
|
||||
|
||||
122
web/app/components/base/chat/__tests__/partialMessages.json
Normal file
122
web/app/components/base/chat/__tests__/partialMessages.json
Normal file
@ -0,0 +1,122 @@
|
||||
[
|
||||
{
|
||||
"id": "question-ebb73fe2-15de-46dd-aab5-75416d8448eb",
|
||||
"content": "123",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418"
|
||||
},
|
||||
{
|
||||
"id": "ebb73fe2-15de-46dd-aab5-75416d8448eb",
|
||||
"content": "237.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-ebb73fe2-15de-46dd-aab5-75416d8448eb"
|
||||
},
|
||||
{
|
||||
"id": "question-3553d508-3850-462e-8594-078539f940f9",
|
||||
"content": "123",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418"
|
||||
},
|
||||
{
|
||||
"id": "3553d508-3850-462e-8594-078539f940f9",
|
||||
"content": "My number is 256.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-3553d508-3850-462e-8594-078539f940f9"
|
||||
},
|
||||
{
|
||||
"id": "question-507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
"content": "123",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "57c989f9-3fa4-4dec-9ee5-c3568dd27418"
|
||||
},
|
||||
{
|
||||
"id": "507f9df9-1f06-4a57-bb38-f00228c42c22",
|
||||
"content": "My number is 259.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-507f9df9-1f06-4a57-bb38-f00228c42c22"
|
||||
},
|
||||
{
|
||||
"id": "question-9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
"content": "1024",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "507f9df9-1f06-4a57-bb38-f00228c42c22"
|
||||
},
|
||||
{
|
||||
"id": "9e51a13b-7780-4565-98dc-f2d8c3b1758f",
|
||||
"content": "My number is 2048.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-9e51a13b-7780-4565-98dc-f2d8c3b1758f"
|
||||
},
|
||||
{
|
||||
"id": "question-93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
"content": "3306",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "9e51a13b-7780-4565-98dc-f2d8c3b1758f"
|
||||
},
|
||||
{
|
||||
"id": "93bac05d-1470-4ac9-b090-fe21cd7c3d55",
|
||||
"content": "My number is 4782.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-93bac05d-1470-4ac9-b090-fe21cd7c3d55"
|
||||
},
|
||||
{
|
||||
"id": "question-a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
"content": "-5",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "93bac05d-1470-4ac9-b090-fe21cd7c3d55"
|
||||
},
|
||||
{
|
||||
"id": "a956de3d-ef95-4d90-84fe-f7a26ef28cd7",
|
||||
"content": "My number is 22.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-a956de3d-ef95-4d90-84fe-f7a26ef28cd7"
|
||||
},
|
||||
{
|
||||
"id": "question-3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
"content": "3.11",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "a956de3d-ef95-4d90-84fe-f7a26ef28cd7"
|
||||
},
|
||||
{
|
||||
"id": "3cded945-855a-4a24-aab7-43c7dd54664c",
|
||||
"content": "My number is 7.89.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-3cded945-855a-4a24-aab7-43c7dd54664c"
|
||||
},
|
||||
{
|
||||
"id": "question-46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
"content": "78",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "3cded945-855a-4a24-aab7-43c7dd54664c"
|
||||
},
|
||||
{
|
||||
"id": "46a49bb9-0881-459e-8c6a-24d20ae48d2f",
|
||||
"content": "My number is 145.",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-46a49bb9-0881-459e-8c6a-24d20ae48d2f"
|
||||
},
|
||||
{
|
||||
"id": "question-5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
"content": "π",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "46a49bb9-0881-459e-8c6a-24d20ae48d2f"
|
||||
},
|
||||
{
|
||||
"id": "5c56a2b3-f057-42a0-9b2c-52a35713cd8c",
|
||||
"content": "My number is 2π (approximately 6.28).",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-5c56a2b3-f057-42a0-9b2c-52a35713cd8c"
|
||||
},
|
||||
{
|
||||
"id": "question-9eac3bcc-8d3b-4e56-a12b-44c34cebc719",
|
||||
"content": "e",
|
||||
"isAnswer": false,
|
||||
"parentMessageId": "5c56a2b3-f057-42a0-9b2c-52a35713cd8c"
|
||||
},
|
||||
{
|
||||
"id": "9eac3bcc-8d3b-4e56-a12b-44c34cebc719",
|
||||
"content": "My number is 3e (approximately 8.15).",
|
||||
"isAnswer": true,
|
||||
"parentMessageId": "question-9eac3bcc-8d3b-4e56-a12b-44c34cebc719"
|
||||
}
|
||||
]
|
||||
@ -7,6 +7,7 @@ import mixedTestMessages from './mixedTestMessages.json'
|
||||
import multiRootNodesMessages from './multiRootNodesMessages.json'
|
||||
import multiRootNodesWithLegacyTestMessages from './multiRootNodesWithLegacyTestMessages.json'
|
||||
import realWorldMessages from './realWorldMessages.json'
|
||||
import partialMessages from './partialMessages.json'
|
||||
|
||||
function visitNode(tree: ChatItemInTree | ChatItemInTree[], path: string): ChatItemInTree {
|
||||
return get(tree, path)
|
||||
@ -256,9 +257,15 @@ describe('build chat item tree and get thread messages', () => {
|
||||
expect(threadMessages6_2).toMatchSnapshot()
|
||||
})
|
||||
|
||||
const partialMessages = (realWorldMessages as ChatItemInTree[]).slice(-10)
|
||||
const tree7 = buildChatItemTree(partialMessages)
|
||||
it('should work with partial messages', () => {
|
||||
const partialMessages1 = (realWorldMessages as ChatItemInTree[]).slice(-10)
|
||||
const tree7 = buildChatItemTree(partialMessages1)
|
||||
it('should work with partial messages 1', () => {
|
||||
expect(tree7).toMatchSnapshot()
|
||||
})
|
||||
|
||||
const partialMessages2 = (partialMessages as ChatItemInTree[])
|
||||
const tree8 = buildChatItemTree(partialMessages2)
|
||||
it('should work with partial messages 2', () => {
|
||||
expect(tree8).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
|
||||
@ -102,21 +102,21 @@ const ChatInputArea = ({
|
||||
setCurrentIndex(historyRef.current.length)
|
||||
handleSend()
|
||||
}
|
||||
else if (e.key === 'ArrowUp' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
||||
// When the up key is pressed, output the previous element
|
||||
else if (e.key === 'ArrowUp' && !e.shiftKey && !e.nativeEvent.isComposing && e.metaKey) {
|
||||
// When the cmd + up key is pressed, output the previous element
|
||||
if (currentIndex > 0) {
|
||||
setCurrentIndex(currentIndex - 1)
|
||||
setQuery(historyRef.current[currentIndex - 1])
|
||||
}
|
||||
}
|
||||
else if (e.key === 'ArrowDown' && !e.shiftKey && !e.nativeEvent.isComposing) {
|
||||
// When the down key is pressed, output the next element
|
||||
else if (e.key === 'ArrowDown' && !e.shiftKey && !e.nativeEvent.isComposing && e.metaKey) {
|
||||
// When the cmd + down key is pressed, output the next element
|
||||
if (currentIndex < historyRef.current.length - 1) {
|
||||
setCurrentIndex(currentIndex + 1)
|
||||
setQuery(historyRef.current[currentIndex + 1])
|
||||
}
|
||||
else if (currentIndex === historyRef.current.length - 1) {
|
||||
// If it is the last element, clear the input box
|
||||
// If it is the last element, clear the input box
|
||||
setCurrentIndex(historyRef.current.length)
|
||||
setQuery('')
|
||||
}
|
||||
|
||||
@ -127,19 +127,16 @@ function buildChatItemTree(allMessages: IChatItem[]): ChatItemInTree[] {
|
||||
lastAppendedLegacyAnswer = answerNode
|
||||
}
|
||||
else {
|
||||
if (!parentMessageId)
|
||||
if (
|
||||
!parentMessageId
|
||||
|| !allMessages.some(item => item.id === parentMessageId) // parent message might not be fetched yet, in this case we will append the question to the root nodes
|
||||
)
|
||||
rootNodes.push(questionNode)
|
||||
else
|
||||
map[parentMessageId]?.children!.push(questionNode)
|
||||
}
|
||||
}
|
||||
|
||||
// If no messages have parentMessageId=null (indicating a root node),
|
||||
// then we likely have a partial chat history. In this case,
|
||||
// use the first available message as the root node.
|
||||
if (rootNodes.length === 0 && allMessages.length > 0)
|
||||
rootNodes.push(map[allMessages[0]!.id]!)
|
||||
|
||||
return rootNodes
|
||||
}
|
||||
|
||||
|
||||
@ -1,17 +1,31 @@
|
||||
import type { CSSProperties, FC } from 'react'
|
||||
import React from 'react'
|
||||
import s from './style.module.css'
|
||||
import { type VariantProps, cva } from 'class-variance-authority'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
type Props = {
|
||||
type?: 'horizontal' | 'vertical'
|
||||
// orientation?: 'left' | 'right' | 'center'
|
||||
const dividerVariants = cva(
|
||||
'bg-divider-regular',
|
||||
{
|
||||
variants: {
|
||||
type: {
|
||||
horizontal: 'w-full h-[0.5px] my-2',
|
||||
vertical: 'w-[1px] h-full mx-2',
|
||||
},
|
||||
},
|
||||
defaultVariants: {
|
||||
type: 'horizontal',
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
type DividerProps = {
|
||||
className?: string
|
||||
style?: CSSProperties
|
||||
}
|
||||
} & VariantProps<typeof dividerVariants>
|
||||
|
||||
const Divider: FC<Props> = ({ type = 'horizontal', className = '', style }) => {
|
||||
const Divider: FC<DividerProps> = ({ type, className = '', style }) => {
|
||||
return (
|
||||
<div className={`${s.divider} ${s[type]} ${className}`} style={style}></div>
|
||||
<div className={classNames(dividerVariants({ type }), className)} style={style}></div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
.divider {
|
||||
@apply bg-gray-200;
|
||||
}
|
||||
.horizontal {
|
||||
@apply w-full h-[0.5px] my-2;
|
||||
}
|
||||
.vertical {
|
||||
@apply w-[1px] h-full mx-2;
|
||||
}
|
||||
@ -47,6 +47,12 @@ gtag('config', '${gaIdMaps[gaType]}');
|
||||
nonce={nonce!}
|
||||
>
|
||||
</Script>
|
||||
{/* Cookie banner */}
|
||||
<Script
|
||||
id="cookieyes"
|
||||
src='https://cdn-cookieyes.com/client_data/2a645945fcae53f8e025a2b1/script.js'
|
||||
nonce={nonce!}
|
||||
></Script>
|
||||
</>
|
||||
|
||||
)
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
|
||||
<path d="M20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22ZM7 6V10H11V6H7ZM7 12V14H17V12H7ZM7 16V18H17V16H7ZM13 7V9H17V7H13Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 292 B |
@ -0,0 +1,23 @@
|
||||
{
|
||||
"icon": {
|
||||
"type": "element",
|
||||
"isRootNode": true,
|
||||
"name": "svg",
|
||||
"attributes": {
|
||||
"xmlns": "http://www.w3.org/2000/svg",
|
||||
"viewBox": "0 0 24 24",
|
||||
"fill": "currentColor"
|
||||
},
|
||||
"children": [
|
||||
{
|
||||
"type": "element",
|
||||
"name": "path",
|
||||
"attributes": {
|
||||
"d": "M20 22H4C3.44772 22 3 21.5523 3 21V3C3 2.44772 3.44772 2 4 2H20C20.5523 2 21 2.44772 21 3V21C21 21.5523 20.5523 22 20 22ZM7 6V10H11V6H7ZM7 12V14H17V12H7ZM7 16V18H17V16H7ZM13 7V9H17V7H13Z"
|
||||
},
|
||||
"children": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"name": "Document"
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
// GENERATE BY script
|
||||
// DON NOT EDIT IT MANUALLY
|
||||
|
||||
import * as React from 'react'
|
||||
import data from './Document.json'
|
||||
import IconBase from '@/app/components/base/icons/IconBase'
|
||||
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||
|
||||
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||
props,
|
||||
ref,
|
||||
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||
|
||||
Icon.displayName = 'Document'
|
||||
|
||||
export default Icon
|
||||
@ -7,3 +7,4 @@ export { default as Microphone01 } from './Microphone01'
|
||||
export { default as TextToAudio } from './TextToAudio'
|
||||
export { default as VirtualAssistant } from './VirtualAssistant'
|
||||
export { default as Vision } from './Vision'
|
||||
export { default as Document } from './Document'
|
||||
|
||||
@ -3,16 +3,19 @@ import type { ReactNode } from 'react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import {
|
||||
CheckCircleIcon,
|
||||
ExclamationTriangleIcon,
|
||||
InformationCircleIcon,
|
||||
XCircleIcon,
|
||||
} from '@heroicons/react/20/solid'
|
||||
RiAlertFill,
|
||||
RiCheckboxCircleFill,
|
||||
RiCloseLine,
|
||||
RiErrorWarningFill,
|
||||
RiInformation2Fill,
|
||||
} from '@remixicon/react'
|
||||
import { createContext, useContext } from 'use-context-selector'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import classNames from '@/utils/classnames'
|
||||
|
||||
export type IToastProps = {
|
||||
type?: 'success' | 'error' | 'warning' | 'info'
|
||||
size?: 'md' | 'sm'
|
||||
duration?: number
|
||||
message: string
|
||||
children?: ReactNode
|
||||
@ -21,60 +24,55 @@ export type IToastProps = {
|
||||
}
|
||||
type IToastContext = {
|
||||
notify: (props: IToastProps) => void
|
||||
close: () => void
|
||||
}
|
||||
|
||||
export const ToastContext = createContext<IToastContext>({} as IToastContext)
|
||||
export const useToastContext = () => useContext(ToastContext)
|
||||
const Toast = ({
|
||||
type = 'info',
|
||||
size = 'md',
|
||||
message,
|
||||
children,
|
||||
className,
|
||||
}: IToastProps) => {
|
||||
const { close } = useToastContext()
|
||||
// sometimes message is react node array. Not handle it.
|
||||
if (typeof message !== 'string')
|
||||
return null
|
||||
|
||||
return <div className={classNames(
|
||||
className,
|
||||
'fixed rounded-md p-4 my-4 mx-8 z-[9999]',
|
||||
'fixed w-[360px] rounded-xl my-4 mx-8 flex-grow z-[9999] overflow-hidden',
|
||||
size === 'md' ? 'p-3' : 'p-2',
|
||||
'border border-components-panel-border-subtle bg-components-panel-bg-blur shadow-sm',
|
||||
'top-0',
|
||||
'right-0',
|
||||
type === 'success' ? 'bg-green-50' : '',
|
||||
type === 'error' ? 'bg-red-50' : '',
|
||||
type === 'warning' ? 'bg-yellow-50' : '',
|
||||
type === 'info' ? 'bg-blue-50' : '',
|
||||
)}>
|
||||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
{type === 'success' && <CheckCircleIcon className="w-5 h-5 text-green-400" aria-hidden="true" />}
|
||||
{type === 'error' && <XCircleIcon className="w-5 h-5 text-red-400" aria-hidden="true" />}
|
||||
{type === 'warning' && <ExclamationTriangleIcon className="w-5 h-5 text-yellow-400" aria-hidden="true" />}
|
||||
{type === 'info' && <InformationCircleIcon className="w-5 h-5 text-blue-400" aria-hidden="true" />}
|
||||
<div className={`absolute inset-0 opacity-40 ${
|
||||
(type === 'success' && 'bg-[linear-gradient(92deg,rgba(23,178,106,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
|
||||
|| (type === 'warning' && 'bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
|
||||
|| (type === 'error' && 'bg-[linear-gradient(92deg,rgba(240,68,56,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
|
||||
|| (type === 'info' && 'bg-[linear-gradient(92deg,rgba(11,165,236,0.25)_0%,rgba(255,255,255,0.00)_100%)]')
|
||||
}`}
|
||||
/>
|
||||
<div className={`flex ${size === 'md' ? 'gap-1' : 'gap-0.5'}`}>
|
||||
<div className={`flex justify-center items-center ${size === 'md' ? 'p-0.5' : 'p-1'}`}>
|
||||
{type === 'success' && <RiCheckboxCircleFill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-success`} aria-hidden="true" />}
|
||||
{type === 'error' && <RiErrorWarningFill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-destructive`} aria-hidden="true" />}
|
||||
{type === 'warning' && <RiAlertFill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-warning-secondary`} aria-hidden="true" />}
|
||||
{type === 'info' && <RiInformation2Fill className={`${size === 'md' ? 'w-5 h-5' : 'w-4 h-4'} text-text-accent`} aria-hidden="true" />}
|
||||
</div>
|
||||
<div className="ml-3">
|
||||
<h3 className={
|
||||
classNames(
|
||||
'text-sm font-medium',
|
||||
type === 'success' ? 'text-green-800' : '',
|
||||
type === 'error' ? 'text-red-800' : '',
|
||||
type === 'warning' ? 'text-yellow-800' : '',
|
||||
type === 'info' ? 'text-blue-800' : '',
|
||||
)
|
||||
}>{message}</h3>
|
||||
{children && <div className={
|
||||
classNames(
|
||||
'mt-2 text-sm',
|
||||
type === 'success' ? 'text-green-700' : '',
|
||||
type === 'error' ? 'text-red-700' : '',
|
||||
type === 'warning' ? 'text-yellow-700' : '',
|
||||
type === 'info' ? 'text-blue-700' : '',
|
||||
)
|
||||
}>
|
||||
<div className={`flex py-1 ${size === 'md' ? 'px-1' : 'px-0.5'} flex-col items-start gap-1 flex-grow`}>
|
||||
<div className='text-text-primary system-sm-semibold'>{message}</div>
|
||||
{children && <div className='text-text-secondary system-xs-regular'>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<ActionButton className='z-[1000]' onClick={close}>
|
||||
<RiCloseLine className='w-4 h-4 flex-shrink-0 text-text-tertiary' />
|
||||
</ActionButton>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -106,6 +104,7 @@ export const ToastProvider = ({
|
||||
setMounted(true)
|
||||
setParams(props)
|
||||
},
|
||||
close: () => setMounted(false),
|
||||
}}>
|
||||
{mounted && <Toast {...params} />}
|
||||
{children}
|
||||
@ -114,16 +113,17 @@ export const ToastProvider = ({
|
||||
|
||||
Toast.notify = ({
|
||||
type,
|
||||
size = 'md',
|
||||
message,
|
||||
duration,
|
||||
className,
|
||||
}: Pick<IToastProps, 'type' | 'message' | 'duration' | 'className'>) => {
|
||||
}: Pick<IToastProps, 'type' | 'size' | 'message' | 'duration' | 'className'>) => {
|
||||
const defaultDuring = (type === 'success' || type === 'info') ? 3000 : 6000
|
||||
if (typeof window === 'object') {
|
||||
const holder = document.createElement('div')
|
||||
const root = createRoot(holder)
|
||||
|
||||
root.render(<Toast type={type} message={message} duration={duration} className={className} />)
|
||||
root.render(<Toast type={type} size={size} message={message} duration={duration} className={className} />)
|
||||
document.body.appendChild(holder)
|
||||
setTimeout(() => {
|
||||
if (holder)
|
||||
|
||||
@ -2,14 +2,19 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { RiListUnordered } from '@remixicon/react'
|
||||
import TemplateEn from './template/template.en.mdx'
|
||||
import TemplateZh from './template/template.zh.mdx'
|
||||
import TemplateJa from './template/template.ja.mdx'
|
||||
import TemplateAdvancedChatEn from './template/template_advanced_chat.en.mdx'
|
||||
import TemplateAdvancedChatZh from './template/template_advanced_chat.zh.mdx'
|
||||
import TemplateAdvancedChatJa from './template/template_advanced_chat.ja.mdx'
|
||||
import TemplateWorkflowEn from './template/template_workflow.en.mdx'
|
||||
import TemplateWorkflowZh from './template/template_workflow.zh.mdx'
|
||||
import TemplateWorkflowJa from './template/template_workflow.ja.mdx'
|
||||
import TemplateChatEn from './template/template_chat.en.mdx'
|
||||
import TemplateChatZh from './template/template_chat.zh.mdx'
|
||||
import TemplateChatJa from './template/template_chat.ja.mdx'
|
||||
import I18n from '@/context/i18n'
|
||||
import { LanguagesSupported } from '@/i18n/language'
|
||||
|
||||
@ -21,6 +26,7 @@ const Doc = ({ appDetail }: IDocProps) => {
|
||||
const { locale } = useContext(I18n)
|
||||
const { t } = useTranslation()
|
||||
const [toc, setToc] = useState<Array<{ href: string; text: string }>>([])
|
||||
const [isTocExpanded, setIsTocExpanded] = useState(false)
|
||||
|
||||
const variables = appDetail?.model_config?.configs?.prompt_variables || []
|
||||
const inputs = variables.reduce((res: any, variable: any) => {
|
||||
@ -28,6 +34,11 @@ const Doc = ({ appDetail }: IDocProps) => {
|
||||
return res
|
||||
}, {})
|
||||
|
||||
useEffect(() => {
|
||||
const mediaQuery = window.matchMedia('(min-width: 1280px)')
|
||||
setIsTocExpanded(mediaQuery.matches)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const extractTOC = () => {
|
||||
const article = document.querySelector('article')
|
||||
@ -50,36 +61,92 @@ const Doc = ({ appDetail }: IDocProps) => {
|
||||
// Run after component has rendered
|
||||
setTimeout(extractTOC, 0)
|
||||
}, [appDetail, locale])
|
||||
|
||||
return (
|
||||
<div className="flex">
|
||||
<nav className="toc w-64 fixed right-8 top-32 bg-gray-50 p-4 rounded-lg shadow-md z-10">
|
||||
<h3 className="text-lg font-semibold mb-4">{t('appApi.develop.toc')}</h3>
|
||||
<ul className="space-y-2">
|
||||
{toc.map((item, index) => (
|
||||
<li key={index}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-gray-600 hover:text-gray-900 hover:underline transition-colors duration-200"
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
<div className={`fixed right-8 top-32 z-10 transition-all ${isTocExpanded ? 'w-64' : 'w-10'}`}>
|
||||
{isTocExpanded
|
||||
? (
|
||||
<nav className="toc w-full bg-gray-50 p-4 rounded-lg shadow-md">
|
||||
<div className="flex justify-between items-center mb-4">
|
||||
<h3 className="text-lg font-semibold">{t('appApi.develop.toc')}</h3>
|
||||
<button
|
||||
onClick={() => setIsTocExpanded(false)}
|
||||
className="text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<ul className="space-y-2">
|
||||
{toc.map((item, index) => (
|
||||
<li key={index}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="text-gray-600 hover:text-gray-900 hover:underline transition-colors duration-200"
|
||||
>
|
||||
{item.text}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
)
|
||||
: (
|
||||
<button
|
||||
onClick={() => setIsTocExpanded(true)}
|
||||
className="w-10 h-10 bg-gray-50 rounded-full shadow-md flex items-center justify-center hover:bg-gray-100 transition-colors duration-200"
|
||||
>
|
||||
<RiListUnordered className="w-6 h-6" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<article className="prose prose-xl" >
|
||||
{(appDetail?.mode === 'chat' || appDetail?.mode === 'agent-chat') && (
|
||||
locale !== LanguagesSupported[1] ? <TemplateChatEn appDetail={appDetail} variables={variables} inputs={inputs} /> : <TemplateChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
(() => {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
})()
|
||||
)}
|
||||
{appDetail?.mode === 'advanced-chat' && (
|
||||
locale !== LanguagesSupported[1] ? <TemplateAdvancedChatEn appDetail={appDetail} variables={variables} inputs={inputs} /> : <TemplateAdvancedChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
(() => {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateAdvancedChatZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateAdvancedChatJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateAdvancedChatEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
})()
|
||||
)}
|
||||
{appDetail?.mode === 'workflow' && (
|
||||
locale !== LanguagesSupported[1] ? <TemplateWorkflowEn appDetail={appDetail} variables={variables} inputs={inputs} /> : <TemplateWorkflowZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
(() => {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateWorkflowZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateWorkflowJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateWorkflowEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
})()
|
||||
)}
|
||||
{appDetail?.mode === 'completion' && (
|
||||
locale !== LanguagesSupported[1] ? <TemplateEn appDetail={appDetail} variables={variables} inputs={inputs} /> : <TemplateZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
(() => {
|
||||
switch (locale) {
|
||||
case LanguagesSupported[1]:
|
||||
return <TemplateZh appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
case LanguagesSupported[7]:
|
||||
return <TemplateJa appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
default:
|
||||
return <TemplateEn appDetail={appDetail} variables={variables} inputs={inputs} />
|
||||
}
|
||||
})()
|
||||
)}
|
||||
</article>
|
||||
</div>
|
||||
|
||||
551
web/app/components/develop/template/template.ja.mdx
Executable file
551
web/app/components/develop/template/template.ja.mdx
Executable file
@ -0,0 +1,551 @@
|
||||
import { CodeGroup } from '../code.tsx'
|
||||
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
|
||||
|
||||
# Completion アプリ API
|
||||
|
||||
テキスト生成アプリケーションはセッションレスをサポートし、翻訳、記事作成、要約AI等に最適です。
|
||||
|
||||
<div>
|
||||
### ベースURL
|
||||
<CodeGroup title="Code" targetCode={props.appDetail.api_base_url}>
|
||||
```javascript
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 認証
|
||||
|
||||
サービスAPIは`API-Key`認証を使用します。
|
||||
<i>**APIキーの漏洩による重大な結果を避けるため、APIキーはサーバーサイドに保存し、クライアントサイドでは共有や保存しないことを強く推奨します。**</i>
|
||||
|
||||
すべてのAPIリクエストで、以下のように`Authorization` HTTPヘッダーにAPIキーを含めてください:
|
||||
|
||||
<CodeGroup title="Code">
|
||||
```javascript
|
||||
Authorization: Bearer {API_KEY}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages'
|
||||
method='POST'
|
||||
title='完了メッセージの作成'
|
||||
name='#Create-Completion-Message'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
テキスト生成アプリケーションにリクエストを送信します。
|
||||
|
||||
### リクエストボディ
|
||||
|
||||
<Properties>
|
||||
|
||||
<Property name='inputs' type='object' key='inputs'>
|
||||
アプリで定義された各種変数値を入力できます。
|
||||
`inputs`パラメータには複数のキー/値ペアが含まれ、各キーは特定の変数に対応し、各値はその変数の具体的な値となります。
|
||||
テキスト生成アプリケーションでは、少なくとも1つのキー/値ペアの入力が必要です。
|
||||
- `query` (string) 必須
|
||||
入力テキスト、処理される内容。
|
||||
</Property>
|
||||
<Property name='response_mode' type='string' key='response_mode'>
|
||||
レスポンス返却モード、以下をサポート:
|
||||
- `streaming` ストリーミングモード(推奨)、SSE([Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events))によるタイプライター風の出力を実装。
|
||||
- `blocking` ブロッキングモード、実行完了後に結果を返却。(処理が長い場合はリクエストが中断される可能性があります)
|
||||
<i>Cloudflareの制限により、100秒後に返却なしで中断されます。</i>
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
ユーザー識別子、エンドユーザーの身元を定義し、取得や統計に使用します。
|
||||
アプリケーション内で開発者が一意に定義する必要があります。
|
||||
</Property>
|
||||
<Property name='files' type='array[object]' key='files'>
|
||||
ファイルリスト、モデルがVision機能をサポートしている場合のみ、テキスト理解と質問応答を組み合わせたファイル(画像)の入力に適しています。
|
||||
- `type` (string) サポートされるタイプ:`image`(現在は画像タイプのみサポート)
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じてアップロードする必要があります(転送方法が`local_file`の場合)
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
`response_mode`が`blocking`の場合、CompletionResponseオブジェクトを返却します。
|
||||
`response_mode`が`streaming`の場合、ChunkCompletionResponseストリームを返却します。
|
||||
|
||||
### ChatCompletionResponse
|
||||
アプリの完全な結果を返却、`Content-Type`は`application/json`です。
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `mode` (string) アプリモード、固定で`chat`
|
||||
- `answer` (string) 完全な応答内容
|
||||
- `metadata` (object) メタデータ
|
||||
- `usage` (Usage) モデル使用情報
|
||||
- `retriever_resources` (array[RetrieverResource]) 引用と帰属のリスト
|
||||
- `created_at` (int) メッセージ作成タイムスタンプ、例:1705395332
|
||||
|
||||
### ChunkChatCompletionResponse
|
||||
アプリが出力するストリームチャンクを返却、`Content-Type`は`text/event-stream`です。
|
||||
各ストリーミングチャンクは`data:`で始まり、2つの改行文字`\n\n`で区切られます:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
ストリーミングチャンクの構造は`event`によって異なります:
|
||||
- `event: message` LLMがテキストチャンクを返すイベント、つまり完全なテキストがチャンク形式で出力されます。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `answer` (string) LLMが返したテキストチャンクの内容
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: message_end` メッセージ終了イベント、このイベントを受信するとストリーミングが終了したことを意味します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `metadata` (object) メタデータ
|
||||
- `usage` (Usage) モデル使用情報
|
||||
- `retriever_resources` (array[RetrieverResource]) 引用と帰属のリスト
|
||||
- `event: tts_message` TTS音声ストリームイベント、つまり音声合成出力。内容はMp3形式の音声ブロックで、base64文字列としてエンコードされています。再生時は単にbase64をデコードしてプレーヤーに供給するだけです。(このメッセージは自動再生が有効な場合のみ利用可能)
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の応答停止インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 音声合成後の音声、base64テキストコンテンツとしてエンコード、再生時は単にbase64をデコードしてプレーヤーに供給
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: tts_message_end` TTS音声ストリーム終了イベント、このイベントを受信すると音声ストリームが終了したことを示します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の応答停止インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 終了イベントには音声がないため、空文字列
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: message_replace` メッセージ内容置換イベント。
|
||||
出力内容のモデレーションが有効な場合、コンテンツがフラグ付けされると、このイベントを通じてメッセージ内容が事前設定された返信に置き換えられます。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `answer` (string) 置換内容(LLMの返信テキストすべてを直接置換)
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: error`
|
||||
ストリーミング処理中に発生した例外は、ストリームイベントの形式で出力され、エラーイベントを受信するとストリームが終了します。
|
||||
- `task_id` (string) タスクID、リクエストの追跡と以下の生成停止APIに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `status` (int) HTTPステータスコード
|
||||
- `code` (string) エラーコード
|
||||
- `message` (string) エラーメッセージ
|
||||
- `event: ping` 接続を維持するため10秒ごとのPingイベント。
|
||||
|
||||
### エラー
|
||||
- 404, 会話が存在しません
|
||||
- 400, `invalid_param`, パラメータ入力異常
|
||||
- 400, `app_unavailable`, アプリ設定が利用できません
|
||||
- 400, `provider_not_initialize`, 利用可能なモデル認証情報設定がありません
|
||||
- 400, `provider_quota_exceeded`, モデル呼び出しクォータ不足
|
||||
- 400, `model_currently_not_support`, 現在のモデルは利用できません
|
||||
- 400, `completion_request_error`, テキスト生成に失敗しました
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": {"query": "Hello, world!"},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/completion-messages' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"inputs": {
|
||||
"query": "Hello, world!"
|
||||
},
|
||||
"response_mode": "streaming",
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
### ブロッキングモード
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"event": "message",
|
||||
"message_id": "9da23599-e713-473b-982c-4328d4f5c78a",
|
||||
"mode": "completion",
|
||||
"answer": "Hello World!...",
|
||||
"metadata": {
|
||||
"usage": {
|
||||
"prompt_tokens": 1033,
|
||||
"prompt_unit_price": "0.001",
|
||||
"prompt_price_unit": "0.001",
|
||||
"prompt_price": "0.0010330",
|
||||
"completion_tokens": 128,
|
||||
"completion_unit_price": "0.002",
|
||||
"completion_price_unit": "0.001",
|
||||
"completion_price": "0.0002560",
|
||||
"total_tokens": 1161,
|
||||
"total_price": "0.0012890",
|
||||
"currency": "USD",
|
||||
"latency": 0.7682376249867957
|
||||
}
|
||||
},
|
||||
"created_at": 1705407629
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### ストリーミングモード
|
||||
<CodeGroup title="Response">
|
||||
```streaming {{ title: 'Response' }}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " I", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": "'m", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " glad", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " to", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " meet", "created_at": 1679586595}
|
||||
data: {"event": "message", "message_id": : "5ad4cb98-f0c7-4085-b384-88c403be6290", "answer": " you", "created_at": 1679586595}
|
||||
data: {"event": "message_end", "id": "5e52ce04-874b-4d27-9045-b3bc80def685", "metadata": {"usage": {"prompt_tokens": 1033, "prompt_unit_price": "0.001", "prompt_price_unit": "0.001", "prompt_price": "0.0010330", "completion_tokens": 135, "completion_unit_price": "0.002", "completion_price_unit": "0.001", "completion_price": "0.0002700", "total_tokens": 1168, "total_price": "0.0013030", "currency": "USD", "latency": 1.381760165997548}}}
|
||||
data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"}
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
<Heading
|
||||
url='/files/upload'
|
||||
method='POST'
|
||||
title='ファイルアップロード'
|
||||
name='#file-upload'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
メッセージ送信時に使用するファイル(現在は画像のみ対応)をアップロードし、画像とテキストのマルチモーダルな理解を可能にします。
|
||||
png、jpg、jpeg、webp、gif形式に対応しています。
|
||||
<i>アップロードされたファイルは、現在のエンドユーザーのみが使用できます。</i>
|
||||
|
||||
### リクエストボディ
|
||||
このインターフェースは`multipart/form-data`リクエストが必要です。
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
|
||||
|
||||
### レスポンス
|
||||
アップロードが成功すると、サーバーはファイルのIDと関連情報を返します。
|
||||
- `id` (uuid) ID
|
||||
- `name` (string) ファイル名
|
||||
- `size` (int) ファイルサイズ(バイト)
|
||||
- `extension` (string) ファイル拡張子
|
||||
- `mime_type` (string) ファイルのMIMEタイプ
|
||||
- `created_by` (uuid) エンドユーザーID
|
||||
- `created_at` (timestamp) 作成タイムスタンプ、例:1705395332
|
||||
|
||||
### エラー
|
||||
- 400, `no_file_uploaded`, ファイルを提供する必要があります
|
||||
- 400, `too_many_files`, 現在は1つのファイルのみ受け付けています
|
||||
- 400, `unsupported_preview`, ファイルがプレビューに対応していません
|
||||
- 400, `unsupported_estimate`, ファイルが推定に対応していません
|
||||
- 413, `file_too_large`, ファイルが大きすぎます
|
||||
- 415, `unsupported_file_type`, サポートされていない拡張子です。現在はドキュメントファイルのみ受け付けています
|
||||
- 503, `s3_connection_failed`, S3サービスに接続できません
|
||||
- 503, `s3_permission_denied`, S3へのファイルアップロード権限がありません
|
||||
- 503, `s3_file_too_large`, ファイルがS3のサイズ制限を超えています
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup title="Request" tag="POST" label="/files/upload" targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--form 'file=@"/path/to/file"'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
### レスポンス例
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"id": "72fa9618-8f89-4a37-9b33-7e1178a24a67",
|
||||
"name": "example.png",
|
||||
"size": 1024,
|
||||
"extension": "png",
|
||||
"mime_type": "image/png",
|
||||
"created_by": "6ad1ab0a-73ff-4ac1-b9e4-cdb312f71f13",
|
||||
"created_at": 1577836800,
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/completion-messages/:task_id/stop'
|
||||
method='POST'
|
||||
title='生成の停止'
|
||||
name='#stop-generatebacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ストリーミングモードでのみサポートされています。
|
||||
### パス
|
||||
- `task_id` (string) タスクID、ストリーミングチャンクの返信から取得可能
|
||||
リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子。エンドユーザーの身元を定義するために使用され、メッセージ送信インターフェースで渡されたユーザーと一致する必要があります。
|
||||
### レスポンス
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup title="Request" tag="POST" label="/completion-messages/:task_id/stop" targetCode={`curl -X POST '${props.appDetail.api_base_url}/completion-messages/:task_id/stop' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{ "user": "abc-123"}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/completion-messages/:task_id/stop' \
|
||||
-H 'Authorization: Bearer {api_key}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### レスポンス例
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/messages/:message_id/feedbacks'
|
||||
method='POST'
|
||||
title='メッセージフィードバック'
|
||||
name='#feedbacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
エンドユーザーはフィードバックメッセージを提供でき、アプリケーション開発者が期待される出力を最適化するのに役立ちます。
|
||||
|
||||
### パス
|
||||
<Properties>
|
||||
<Property name='message_id' type='string' key='message_id'>
|
||||
メッセージID
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### リクエストボディ
|
||||
|
||||
<Properties>
|
||||
<Property name='rating' type='string' key='rating'>
|
||||
高評価は`like`、低評価は`dislike`、高評価の取り消しは`null`
|
||||
</Property>
|
||||
<Property name='user' type='string' key='user'>
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/messages/:message_id/feedbacks" targetCode={`curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks \\\n --header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "rating": "like",\n "user": "abc-123"\n}'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/messages/:message_id/feedbacks' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"rating": "like",
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
<CodeGroup title="Response">
|
||||
```json {{ title: 'Response' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/parameters'
|
||||
method='GET'
|
||||
title='アプリケーション情報の取得'
|
||||
name='#parameters'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ページ開始時に、機能、入力パラメータ名、タイプ、デフォルト値などの情報を取得するために使用されます。
|
||||
|
||||
### クエリ
|
||||
|
||||
<Properties>
|
||||
<Property name='user' type='string' key='user'>
|
||||
開発者のルールで定義されたユーザー識別子。アプリケーション内で一意である必要があります。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### レスポンス
|
||||
- `opening_statement` (string) 開始文
|
||||
- `suggested_questions` (array[string]) 開始時の提案質問リスト
|
||||
- `suggested_questions_after_answer` (object) 回答後の提案質問を有効にします。
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `speech_to_text` (object) 音声からテキスト
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `retriever_resource` (object) 引用と帰属
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `annotation_reply` (object) 注釈付き返信
|
||||
- `enabled` (bool) 有効かどうか
|
||||
- `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' }}
|
||||
{
|
||||
"opening_statement": "Hello!",
|
||||
"suggested_questions_after_answer": {
|
||||
"enabled": true
|
||||
},
|
||||
"speech_to_text": {
|
||||
"enabled": true
|
||||
},
|
||||
"retriever_resource": {
|
||||
"enabled": true
|
||||
},
|
||||
"annotation_reply": {
|
||||
"enabled": true
|
||||
},
|
||||
"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='/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>
|
||||
1105
web/app/components/develop/template/template_advanced_chat.ja.mdx
Normal file
1105
web/app/components/develop/template/template_advanced_chat.ja.mdx
Normal file
File diff suppressed because it is too large
Load Diff
1134
web/app/components/develop/template/template_chat.ja.mdx
Normal file
1134
web/app/components/develop/template/template_chat.ja.mdx
Normal file
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,7 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
User identifier, used to define the identity of the end-user for retrieval and statistics.
|
||||
Should be uniquely defined by the developer within the application.
|
||||
- `files` (array[object]) Optional
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports Vision capability.
|
||||
File list, suitable for inputting files combined with text understanding and answering questions, available only when the model supports file parsing and understanding capability.
|
||||
- `type` (string) Supported type:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
@ -188,6 +188,19 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
<CodeGroup title="File variable example">
|
||||
```json {{ title: 'File variable example' }}
|
||||
{
|
||||
"inputs": {
|
||||
"{variable_name}": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": "{upload_file_id}",
|
||||
"type": "{document_type}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### Blocking Mode
|
||||
<CodeGroup title="Response">
|
||||
@ -223,7 +236,88 @@ Workflow applications offers non-session support and is ideal for translation, a
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="File upload sample code">
|
||||
```json {{ title: 'File upload sample code' }}
|
||||
{
|
||||
import requests
|
||||
import json
|
||||
|
||||
def upload_file(file_path, user):
|
||||
upload_url = "https://api.dify.ai/v1/files/upload"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxx",
|
||||
}
|
||||
|
||||
try:
|
||||
print("Upload file...")
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {
|
||||
'file': (file_path, file, 'text/plain') # Make sure the file is uploaded with the appropriate MIME type
|
||||
}
|
||||
data = {
|
||||
"user": user,
|
||||
"type": "TXT" # Set the file type to TXT
|
||||
}
|
||||
|
||||
response = requests.post(upload_url, headers=headers, files=files, data=data)
|
||||
if response.status_code == 201: # 201 means creation is successful
|
||||
print("File uploaded successfully")
|
||||
return response.json().get("id") # Get the uploaded file ID
|
||||
else:
|
||||
print(f"File upload failed, status code: {response.status_code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"Error occurred: {str(e)}")
|
||||
return None
|
||||
|
||||
def run_workflow(file_id, user, response_mode="blocking"):
|
||||
workflow_url = "https://api.dify.ai/v1/workflows/run"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxxx",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
data = {
|
||||
"inputs": {
|
||||
"orig_mail": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": file_id,
|
||||
"type": "document"
|
||||
}
|
||||
},
|
||||
"response_mode": response_mode,
|
||||
"user": user
|
||||
}
|
||||
|
||||
try:
|
||||
print("Run Workflow...")
|
||||
response = requests.post(workflow_url, headers=headers, json=data)
|
||||
if response.status_code == 200:
|
||||
print("Workflow execution successful")
|
||||
return response.json()
|
||||
else:
|
||||
print(f"Workflow execution failed, status code: {response.status_code}")
|
||||
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
|
||||
except Exception as e:
|
||||
print(f"Error occurred: {str(e)}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
# Usage Examples
|
||||
file_path = "{your_file_path}"
|
||||
user = "difyuser"
|
||||
|
||||
# Upload files
|
||||
file_id = upload_file(file_path, user)
|
||||
if file_id:
|
||||
# The file was uploaded successfully, and the workflow continues to run
|
||||
result = run_workflow(file_id, user)
|
||||
print(result)
|
||||
else:
|
||||
print("File upload failed and workflow cannot be executed")
|
||||
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
701
web/app/components/develop/template/template_workflow.ja.mdx
Normal file
701
web/app/components/develop/template/template_workflow.ja.mdx
Normal file
@ -0,0 +1,701 @@
|
||||
import { CodeGroup } from '../code.tsx'
|
||||
import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from '../md.tsx'
|
||||
|
||||
# ワークフローアプリAPI
|
||||
|
||||
ワークフローアプリケーションは、セッションをサポートせず、翻訳、記事作成、要約AIなどに最適です。
|
||||
|
||||
<div>
|
||||
### ベースURL
|
||||
<CodeGroup title="コード" targetCode={props.appDetail.api_base_url}>
|
||||
```javascript
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 認証
|
||||
|
||||
サービスAPIは`API-Key`認証を使用します。
|
||||
<i>**APIキーの漏洩を防ぐため、APIキーはクライアント側で共有または保存せず、サーバー側で保存することを強くお勧めします。**</i>
|
||||
|
||||
すべてのAPIリクエストにおいて、以下のように`Authorization`HTTPヘッダーにAPIキーを含めてください:
|
||||
|
||||
<CodeGroup title="コード">
|
||||
```javascript
|
||||
Authorization: Bearer {API_KEY}
|
||||
|
||||
```
|
||||
</CodeGroup>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/workflows/run'
|
||||
method='POST'
|
||||
title='ワークフローを実行'
|
||||
name='#Execute-Workflow'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ワークフローを実行します。公開されたワークフローがないと実行できません。
|
||||
|
||||
### リクエストボディ
|
||||
- `inputs` (object) 必須
|
||||
アプリで定義されたさまざまな変数値の入力を許可します。
|
||||
`inputs`パラメータには複数のキー/値ペアが含まれ、各キーは特定の変数に対応し、各値はその変数の特定の値です。
|
||||
ワークフローアプリケーションは少なくとも1つのキー/値ペアの入力を必要とします。
|
||||
変数がファイルタイプの場合、以下の`files`で説明されているキーを持つオブジェクトを指定してください。
|
||||
- `response_mode` (string) 必須
|
||||
応答の返却モードを指定します。サポートされているモード:
|
||||
- `streaming` ストリーミングモード(推奨)、SSE([Server-Sent Events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events))を通じてタイプライターのような出力を実装します。
|
||||
- `blocking` ブロッキングモード、実行完了後に結果を返します。(プロセスが長い場合、リクエストが中断される可能性があります)
|
||||
<i>Cloudflareの制限により、100秒後に応答がない場合、リクエストは中断されます。</i>
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用されます。
|
||||
アプリケーション内で開発者によって一意に定義される必要があります。
|
||||
- `files` (array[object]) オプション
|
||||
ファイルリストは、テキスト理解と質問への回答を組み合わせたファイルの入力に適しています。モデルがファイルの解析と理解機能をサポートしている場合にのみ使用できます。
|
||||
- `type` (string) サポートされているタイプ:
|
||||
- `document` ('TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB')
|
||||
- `image` ('JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG')
|
||||
- `audio` ('MP3', 'M4A', 'WAV', 'WEBM', 'AMR')
|
||||
- `video` ('MP4', 'MOV', 'MPEG', 'MPGA')
|
||||
- `transfer_method` (string) 転送方法、画像URLの場合は`remote_url` / ファイルアップロードの場合は`local_file`
|
||||
- `url` (string) 画像URL(転送方法が`remote_url`の場合)
|
||||
- `upload_file_id` (string) アップロードされたファイルID、事前にファイルアップロードAPIを通じて取得する必要があります(転送方法が`local_file`の場合)
|
||||
|
||||
### 応答
|
||||
`response_mode`が`blocking`の場合、CompletionResponseオブジェクトを返します。
|
||||
`response_mode`が`streaming`の場合、ChunkCompletionResponseストリームを返します。
|
||||
|
||||
### CompletionResponse
|
||||
アプリの結果を返します。`Content-Type`は`application/json`です。
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `data` (object) 結果の詳細
|
||||
- `id` (string) ワークフロー実行のID
|
||||
- `workflow_id` (string) 関連するワークフローのID
|
||||
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
|
||||
- `outputs` (json) オプションの出力内容
|
||||
- `error` (string) オプションのエラー理由
|
||||
- `elapsed_time` (float) オプションの使用時間(秒)
|
||||
- `total_tokens` (int) オプションの使用トークン数
|
||||
- `total_steps` (int) デフォルト0
|
||||
- `created_at` (timestamp) 開始時間
|
||||
- `finished_at` (timestamp) 終了時間
|
||||
|
||||
### ChunkCompletionResponse
|
||||
アプリによって出力されたストリームチャンクを返します。`Content-Type`は`text/event-stream`です。
|
||||
各ストリーミングチャンクは`data:`で始まり、2つの改行文字`\n\n`で区切られます。以下のように表示されます:
|
||||
<CodeGroup>
|
||||
```streaming {{ title: '応答' }}
|
||||
data: {"event": "message", "task_id": "900bbd43-dc0b-4383-a372-aa6e6c414227", "id": "663c5084-a254-4040-8ad3-51f2a3c1a77c", "answer": "Hi", "created_at": 1705398420}\n\n
|
||||
```
|
||||
</CodeGroup>
|
||||
ストリーミングチャンクの構造は`event`に応じて異なります:
|
||||
- `event: workflow_started` ワークフローが実行を開始
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `event` (string) `workflow_started`に固定
|
||||
- `data` (object) 詳細
|
||||
- `id` (string) ワークフロー実行の一意のID
|
||||
- `workflow_id` (string) 関連するワークフローのID
|
||||
- `sequence_number` (int) 自己増加シリアル番号、アプリ内で自己増加し、1から始まります
|
||||
- `created_at` (timestamp) 作成タイムスタンプ、例:1705395332
|
||||
- `event: node_started` ノード実行開始
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `event` (string) `node_started`に固定
|
||||
- `data` (object) 詳細
|
||||
- `id` (string) ワークフロー実行の一意のID
|
||||
- `node_id` (string) ノードのID
|
||||
- `node_type` (string) ノードのタイプ
|
||||
- `title` (string) ノードの名前
|
||||
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
|
||||
- `predecessor_node_id` (string) オプションのプレフィックスノードID、キャンバス表示実行パスに使用
|
||||
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
|
||||
- `created_at` (timestamp) 開始のタイムスタンプ、例:1705395332
|
||||
- `event: node_finished` ノード実行終了、同じイベントで異なる状態で成功または失敗
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `event` (string) `node_finished`に固定
|
||||
- `data` (object) 詳細
|
||||
- `id` (string) ワークフロー実行の一意のID
|
||||
- `node_id` (string) ノードのID
|
||||
- `node_type` (string) ノードのタイプ
|
||||
- `title` (string) ノードの名前
|
||||
- `index` (int) 実行シーケンス番号、トレースノードシーケンスを表示するために使用
|
||||
- `predecessor_node_id` (string) オプションのプレフィックスノードID、キャンバス表示実行パスに使用
|
||||
- `inputs` (array[object]) ノードで使用されるすべての前のノード変数の内容
|
||||
- `process_data` (json) オプションのノードプロセスデータ
|
||||
- `outputs` (json) オプションの出力内容
|
||||
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
|
||||
- `error` (string) オプションのエラー理由
|
||||
- `elapsed_time` (float) オプションの使用時間(秒)
|
||||
- `execution_metadata` (json) メタデータ
|
||||
- `total_tokens` (int) オプションの使用トークン数
|
||||
- `total_price` (decimal) オプションの総コスト
|
||||
- `currency` (string) オプション 例:`USD` / `RMB`
|
||||
- `created_at` (timestamp) 開始のタイムスタンプ、例:1705395332
|
||||
- `event: workflow_finished` ワークフロー実行終了、同じイベントで異なる状態で成功または失敗
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下のStop Generate APIに使用
|
||||
- `workflow_run_id` (string) ワークフロー実行の一意のID
|
||||
- `event` (string) `workflow_finished`に固定
|
||||
- `data` (object) 詳細
|
||||
- `id` (string) ワークフロー実行のID
|
||||
- `workflow_id` (string) 関連するワークフローのID
|
||||
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
|
||||
- `outputs` (json) オプションの出力内容
|
||||
- `error` (string) オプションのエラー理由
|
||||
- `elapsed_time` (float) オプションの使用時間(秒)
|
||||
- `total_tokens` (int) オプションの使用トークン数
|
||||
- `total_steps` (int) デフォルト0
|
||||
- `created_at` (timestamp) 開始時間
|
||||
- `finished_at` (timestamp) 終了時間
|
||||
- `event: tts_message` TTSオーディオストリームイベント、つまり音声合成出力。内容はMp3形式のオーディオブロックで、base64文字列としてエンコードされています。再生時には、base64をデコードしてプレーヤーに入力するだけです。(このメッセージは自動再生が有効な場合にのみ利用可能)
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下の停止応答インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 音声合成後のオーディオ、base64テキストコンテンツとしてエンコードされており、再生時にはbase64をデコードしてプレーヤーに入力するだけです
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: tts_message_end` TTSオーディオストリーム終了イベント。このイベントを受信すると、オーディオストリームの終了を示します。
|
||||
- `task_id` (string) タスクID、リクエスト追跡と以下の停止応答インターフェースに使用
|
||||
- `message_id` (string) 一意のメッセージID
|
||||
- `audio` (string) 終了イベントにはオーディオがないため、これは空の文字列です
|
||||
- `created_at` (int) 作成タイムスタンプ、例:1705395332
|
||||
- `event: ping` 接続を維持するために10秒ごとに送信されるPingイベント。
|
||||
|
||||
### エラー
|
||||
- 400, `invalid_param`, 異常なパラメータ入力
|
||||
- 400, `app_unavailable`, アプリの設定が利用できません
|
||||
- 400, `provider_not_initialize`, 利用可能なモデル資格情報の設定がありません
|
||||
- 400, `provider_quota_exceeded`, モデル呼び出しのクォータが不足しています
|
||||
- 400, `model_currently_not_support`, 現在のモデルは利用できません
|
||||
- 400, `workflow_request_error`, ワークフロー実行に失敗しました
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup title="リクエスト" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/workflows/run' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"inputs": {},
|
||||
"response_mode": "streaming",
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
<CodeGroup title="ファイル変数の例">
|
||||
```json {{ title: 'ファイル変数の例' }}
|
||||
{
|
||||
"inputs": {
|
||||
"{variable_name}": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": "{upload_file_id}",
|
||||
"type": "{document_type}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### ブロッキングモード
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"workflow_run_id": "djflajgkldjgd",
|
||||
"task_id": "9da23599-e713-473b-982c-4328d4f5c78a",
|
||||
"data": {
|
||||
"id": "fdlsjfjejkghjda",
|
||||
"workflow_id": "fldjaslkfjlsda",
|
||||
"status": "succeeded",
|
||||
"outputs": {
|
||||
"text": "Nice to meet you."
|
||||
},
|
||||
"error": null,
|
||||
"elapsed_time": 0.875,
|
||||
"total_tokens": 3562,
|
||||
"total_steps": 8,
|
||||
"created_at": 1705407629,
|
||||
"finished_at": 1727807631
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### ストリーミングモード
|
||||
<CodeGroup title="応答">
|
||||
```streaming {{ title: '応答' }}
|
||||
data: {"event": "workflow_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "sequence_number": 1, "created_at": 1679586595}}
|
||||
data: {"event": "node_started", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "created_at": 1679586595}}
|
||||
data: {"event": "node_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "node_id": "dfjasklfjdslag", "node_type": "start", "title": "Start", "index": 0, "predecessor_node_id": "fdljewklfklgejlglsd", "inputs": {}, "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "execution_metadata": {"total_tokens": 63127864, "total_price": 2.378, "currency": "USD"}, "created_at": 1679586595}}
|
||||
data: {"event": "workflow_finished", "task_id": "5ad4cb98-f0c7-4085-b384-88c403be6290", "workflow_run_id": "5ad498-f0c7-4085-b384-88cbe6290", "data": {"id": "5ad498-f0c7-4085-b384-88cbe6290", "workflow_id": "dfjasklfjdslag", "outputs": {}, "status": "succeeded", "elapsed_time": 0.324, "total_tokens": 63127864, "total_steps": "1", "created_at": 1679586595, "finished_at": 1679976595}}
|
||||
data: {"event": "tts_message", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": "qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq"}
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="ファイルアップロードのサンプルコード">
|
||||
```json {{ title: 'ファイルアップロードのサンプルコード' }}
|
||||
{
|
||||
import requests
|
||||
import json
|
||||
|
||||
def upload_file(file_path, user):
|
||||
upload_url = "https://api.dify.ai/v1/files/upload"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxx",
|
||||
}
|
||||
|
||||
try:
|
||||
print("ファイルをアップロードしています...")
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {
|
||||
'file': (file_path, file, 'text/plain') # ファイルが適切な MIME タイプでアップロードされていることを確認してください
|
||||
}
|
||||
data = {
|
||||
"user": user,
|
||||
"type": "TXT" # ファイルタイプをTXTに設定します
|
||||
}
|
||||
|
||||
response = requests.post(upload_url, headers=headers, files=files, data=data)
|
||||
if response.status_code == 201: # 201 は作成が成功したことを意味します
|
||||
print("ファイルが正常にアップロードされました")
|
||||
return response.json().get("id") # アップロードされたファイルIDを取得する
|
||||
else:
|
||||
print(f"ファイルのアップロードに失敗しました。ステータス コード: {response.status_code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"エラーが発生しました: {str(e)}")
|
||||
return None
|
||||
|
||||
def run_workflow(file_id, user, response_mode="blocking"):
|
||||
workflow_url = "https://api.dify.ai/v1/workflows/run"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxxx",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
data = {
|
||||
"inputs": {
|
||||
"orig_mail": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": file_id,
|
||||
"type": "document"
|
||||
}
|
||||
},
|
||||
"response_mode": response_mode,
|
||||
"user": user
|
||||
}
|
||||
|
||||
try:
|
||||
print("ワークフローを実行...")
|
||||
response = requests.post(workflow_url, headers=headers, json=data)
|
||||
if response.status_code == 200:
|
||||
print("ワークフローが正常に実行されました")
|
||||
return response.json()
|
||||
else:
|
||||
print(f"ワークフローの実行がステータス コードで失敗しました: {response.status_code}")
|
||||
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
|
||||
except Exception as e:
|
||||
print(f"エラーが発生しました: {str(e)}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
# 使用例
|
||||
file_path = "{your_file_path}"
|
||||
user = "difyuser"
|
||||
|
||||
# ファイルをアップロードする
|
||||
file_id = upload_file(file_path, user)
|
||||
if file_id:
|
||||
# ファイルは正常にアップロードされました。ワークフローの実行を続行します
|
||||
result = run_workflow(file_id, user)
|
||||
print(result)
|
||||
else:
|
||||
print("ファイルのアップロードに失敗し、ワークフローを実行できません")
|
||||
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/workflows/run/:workflow_id'
|
||||
method='GET'
|
||||
title='ワークフロー実行詳細を取得'
|
||||
name='#get-workflow-run-detail'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ワークフロー実行IDに基づいて、ワークフロータスクの現在の実行結果を取得します。
|
||||
### パス
|
||||
- `workflow_id` (string) ワークフローID、ストリーミングチャンクの返り値から取得可能
|
||||
### 応答
|
||||
- `id` (string) ワークフロー実行のID
|
||||
- `workflow_id` (string) 関連するワークフローのID
|
||||
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
|
||||
- `inputs` (json) 入力内容
|
||||
- `outputs` (json) 出力内容
|
||||
- `error` (string) エラー理由
|
||||
- `total_steps` (int) タスクの総ステップ数
|
||||
- `total_tokens` (int) 使用されるトークンの総数
|
||||
- `created_at` (timestamp) 開始時間
|
||||
- `finished_at` (timestamp) 終了時間
|
||||
- `elapsed_time` (float) 使用される総秒数
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup title="リクエスト" tag="GET" label="/workflows/run/:workflow_id" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/workflows/run/:workflow_id' \
|
||||
-H 'Authorization: Bearer {api_key}' \
|
||||
-H 'Content-Type: application/json'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 応答例
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"id": "b1ad3277-089e-42c6-9dff-6820d94fbc76",
|
||||
"workflow_id": "19eff89f-ec03-4f75-b0fc-897e7effea02",
|
||||
"status": "succeeded",
|
||||
"inputs": "{\"sys.files\": [], \"sys.user_id\": \"abc-123\"}",
|
||||
"outputs": null,
|
||||
"error": null,
|
||||
"total_steps": 3,
|
||||
"total_tokens": 0,
|
||||
"created_at": "Thu, 18 Jul 2024 03:17:40 -0000",
|
||||
"finished_at": "Thu, 18 Jul 2024 03:18:10 -0000",
|
||||
"elapsed_time": 30.098514399956912
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/workflows/tasks/:task_id/stop'
|
||||
method='POST'
|
||||
title='生成を停止'
|
||||
name='#stop-generatebacks'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ストリーミングモードでのみサポートされています。
|
||||
### パス
|
||||
- `task_id` (string) タスクID、ストリーミングチャンクの返り値から取得可能
|
||||
### リクエストボディ
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、エンドユーザーのアイデンティティを定義するために使用され、送信メッセージインターフェースで渡されたユーザーと一致している必要があります。
|
||||
### 応答
|
||||
- `result` (string) 常に"success"を返します
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup title="リクエスト" tag="POST" label="/workflows/tasks/:task_id/stop" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \\\n-H 'Authorization: Bearer {api_key}' \\\n-H 'Content-Type: application/json' \\\n--data-raw '{"user": "abc-123"}'`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/workflows/tasks/:task_id/stop' \
|
||||
-H 'Authorization: Bearer {api_key}' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
### 応答例
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"result": "success"
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/files/upload'
|
||||
method='POST'
|
||||
title='ファイルアップロード'
|
||||
name='#file-upload'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
メッセージ送信時に使用するためのファイルをアップロードし、画像とテキストのマルチモーダル理解を可能にします。
|
||||
ワークフローでサポートされている任意の形式をサポートします。
|
||||
アップロードされたファイルは、現在のエンドユーザーのみが使用できます。
|
||||
|
||||
### リクエストボディ
|
||||
このインターフェースは`multipart/form-data`リクエストを必要とします。
|
||||
- `file` (File) 必須
|
||||
アップロードするファイル。
|
||||
- `user` (string) 必須
|
||||
ユーザー識別子、開発者のルールで定義され、アプリケーション内で一意でなければなりません。
|
||||
|
||||
### 応答
|
||||
アップロードが成功すると、サーバーはファイルのIDと関連情報を返します。
|
||||
- `id` (uuid) ID
|
||||
- `name` (string) ファイル名
|
||||
- `size` (int) ファイルサイズ(バイト)
|
||||
- `extension` (string) ファイル拡張子
|
||||
- `mime_type` (string) ファイルのMIMEタイプ
|
||||
- `created_by` (uuid) エンドユーザーID
|
||||
- `created_at` (timestamp) 作成タイムスタンプ、例:1705395332
|
||||
|
||||
### エラー
|
||||
- 400, `no_file_uploaded`, ファイルが提供されていません
|
||||
- 400, `too_many_files`, 現在は1つのファイルのみ受け付けています
|
||||
- 400, `unsupported_preview`, ファイルはプレビューをサポートしていません
|
||||
- 400, `unsupported_estimate`, ファイルは推定をサポートしていません
|
||||
- 413, `file_too_large`, ファイルが大きすぎます
|
||||
- 415, `unsupported_file_type`, サポートされていない拡張子、現在はドキュメントファイルのみ受け付けています
|
||||
- 503, `s3_connection_failed`, S3サービスに接続できません
|
||||
- 503, `s3_permission_denied`, S3にファイルをアップロードする権限がありません
|
||||
- 503, `s3_file_too_large`, ファイルがS3のサイズ制限を超えています
|
||||
- 500, 内部サーバーエラー
|
||||
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
### リクエスト例
|
||||
<CodeGroup title="リクエスト" tag="POST" label="/files/upload" targetCode={`curl -X POST '${props.appDetail.api_base_url}/files/upload' \\\n--header 'Authorization: Bearer {api_key}' \\\n--form 'file=@localfile;type=image/[png|jpeg|jpg|webp|gif] \\\n--form 'user=abc-123'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/files/upload' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
--form 'file=@"/path/to/file"'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
|
||||
### 応答例
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"id": "72fa9618-8f89-4a37-9b33-7e1178a24a67",
|
||||
"name": "example.png",
|
||||
"size": 1024,
|
||||
"extension": "png",
|
||||
"mime_type": "image/png",
|
||||
"created_by": "6ad1ab0a-73ff-4ac1-b9e4-cdb312f71f13",
|
||||
"created_at": 1577836800,
|
||||
}
|
||||
```
|
||||
</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>
|
||||
|
||||
---
|
||||
|
||||
<Heading
|
||||
url='/workflows/logs'
|
||||
method='GET'
|
||||
title='ワークフローログを取得'
|
||||
name='#Get-Workflow-Logs'
|
||||
/>
|
||||
<Row>
|
||||
<Col>
|
||||
ワークフローログを返します。最初のページは最新の`{limit}`メッセージを返します。つまり、逆順です。
|
||||
|
||||
### クエリ
|
||||
|
||||
<Properties>
|
||||
<Property name='keyword' type='string' key='keyword'>
|
||||
検索するキーワード
|
||||
</Property>
|
||||
<Property name='status' type='string' key='status'>
|
||||
succeeded/failed/stopped
|
||||
</Property>
|
||||
<Property name='page' type='int' key='page'>
|
||||
現在のページ、デフォルトは1。
|
||||
</Property>
|
||||
<Property name='limit' type='int' key='limit'>
|
||||
1回のリクエストで返すチャット履歴メッセージの数、デフォルトは20。
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
### 応答
|
||||
- `page` (int) 現在のページ
|
||||
- `limit` (int) 返されたアイテムの数、入力がシステム制限を超える場合、システム制限量を返します
|
||||
- `total` (int) 合計アイテム数
|
||||
- `has_more` (bool) 次のページがあるかどうか
|
||||
- `data` (array[object]) ログリスト
|
||||
- `id` (string) ID
|
||||
- `workflow_run` (object) ワークフロー実行
|
||||
- `id` (string) ID
|
||||
- `version` (string) バージョン
|
||||
- `status` (string) 実行のステータス、`running` / `succeeded` / `failed` / `stopped`
|
||||
- `error` (string) オプションのエラー理由
|
||||
- `elapsed_time` (float) 使用される総秒数
|
||||
- `total_tokens` (int) 使用されるトークン数
|
||||
- `total_steps` (int) デフォルト0
|
||||
- `created_at` (timestamp) 開始時間
|
||||
- `finished_at` (timestamp) 終了時間
|
||||
- `created_from` (string) 作成元
|
||||
- `created_by_role` (string) 作成者の役割
|
||||
- `created_by_account` (string) オプションの作成者アカウント
|
||||
- `created_by_end_user` (object) エンドユーザーによって作成
|
||||
- `id` (string) ID
|
||||
- `type` (string) タイプ
|
||||
- `is_anonymous` (bool) 匿名かどうか
|
||||
- `session_id` (string) セッションID
|
||||
- `created_at` (timestamp) 作成時間
|
||||
</Col>
|
||||
<Col sticky>
|
||||
|
||||
<CodeGroup title="リクエスト" tag="GET" label="/workflows/logs" targetCode={`curl -X GET '${props.appDetail.api_base_url}/workflows/logs'\\\n --header 'Authorization: Bearer {api_key}'`}>
|
||||
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X GET '${props.appDetail.api_base_url}/workflows/logs?limit=1'
|
||||
--header 'Authorization: Bearer {api_key}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
### 応答例
|
||||
<CodeGroup title="応答">
|
||||
```json {{ title: '応答' }}
|
||||
{
|
||||
"page": 1,
|
||||
"limit": 1,
|
||||
"total": 7,
|
||||
"has_more": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "e41b93f1-7ca2-40fd-b3a8-999aeb499cc0",
|
||||
"workflow_run": {
|
||||
"id": "c0640fc8-03ef-4481-a96c-8a13b732a36e",
|
||||
"version": "2024-08-01 12:17:09.771832",
|
||||
"status": "succeeded",
|
||||
"error": null,
|
||||
"elapsed_time": 1.3588523610014818,
|
||||
"total_tokens": 0,
|
||||
"total_steps": 3,
|
||||
"created_at": 1726139643,
|
||||
"finished_at": 1726139644
|
||||
},
|
||||
"created_from": "service-api",
|
||||
"created_by_role": "end_user",
|
||||
"created_by_account": null,
|
||||
"created_by_end_user": {
|
||||
"id": "7f7d9117-dd9d-441d-8970-87e5e7e687a3",
|
||||
"type": "service_api",
|
||||
"is_anonymous": false,
|
||||
"session_id": "abc-123"
|
||||
},
|
||||
"created_at": 1726139644
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
@ -52,7 +52,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
用户标识,用于定义终端用户的身份,方便检索、统计。
|
||||
由开发者定义规则,需保证用户标识在应用内唯一。
|
||||
- `files` (array[object]) Optional
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持 Vision 能力时可用。
|
||||
文件列表,适用于传入文件结合文本理解并回答问题,仅当模型支持该类型文件解析能力时可用。
|
||||
- `type` (string) 支持类型:
|
||||
- `document` 具体类型包含:'TXT', 'MD', 'MARKDOWN', 'PDF', 'HTML', 'XLSX', 'XLS', 'DOCX', 'CSV', 'EML', 'MSG', 'PPTX', 'PPT', 'XML', 'EPUB'
|
||||
- `image` 具体类型包含:'JPG', 'JPEG', 'PNG', 'GIF', 'WEBP', 'SVG'
|
||||
@ -171,8 +171,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
|
||||
</Col>
|
||||
<Col sticky>
|
||||
<CodeGroup title="Request" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
|
||||
<CodeGroup title="Request" tag="POST" label="/workflows/run" targetCode={`curl -X POST '${props.appDetail.api_base_url}/workflows/run' \\\n--header 'Authorization: Bearer {api_key}' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n "inputs": ${JSON.stringify(props.inputs)},\n "response_mode": "streaming",\n "user": "abc-123"\n}'\n`}>
|
||||
```bash {{ title: 'cURL' }}
|
||||
curl -X POST '${props.appDetail.api_base_url}/workflows/run' \
|
||||
--header 'Authorization: Bearer {api_key}' \
|
||||
@ -183,7 +182,19 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
"user": "abc-123"
|
||||
}'
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
<CodeGroup title="File variable example">
|
||||
```json {{ title: 'File variable example' }}
|
||||
{
|
||||
"inputs": {
|
||||
"{variable_name}": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": "{upload_file_id}",
|
||||
"type": "{document_type}"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
### Blocking Mode
|
||||
<CodeGroup title="Response">
|
||||
@ -219,7 +230,88 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
|
||||
data: {"event": "tts_message_end", "conversation_id": "23dd85f3-1a41-4ea0-b7a9-062734ccfaf9", "message_id": "a8bdc41c-13b2-4c18-bfd9-054b9803038c", "created_at": 1721205487, "task_id": "3bf8a0bb-e73b-4690-9e66-4e429bad8ee7", "audio": ""}
|
||||
```
|
||||
</CodeGroup>
|
||||
<CodeGroup title="File upload sample code">
|
||||
```json {{ title: 'File upload sample code' }}
|
||||
{
|
||||
import requests
|
||||
import json
|
||||
|
||||
def upload_file(file_path, user):
|
||||
upload_url = "https://api.dify.ai/v1/files/upload"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxx",
|
||||
}
|
||||
|
||||
try:
|
||||
print("上传文件中...")
|
||||
with open(file_path, 'rb') as file:
|
||||
files = {
|
||||
'file': (file_path, file, 'text/plain') # 确保文件以适当的MIME类型上传
|
||||
}
|
||||
data = {
|
||||
"user": user,
|
||||
"type": "TXT" # 设置文件类型为TXT
|
||||
}
|
||||
|
||||
response = requests.post(upload_url, headers=headers, files=files, data=data)
|
||||
if response.status_code == 201: # 201 表示创建成功
|
||||
print("文件上传成功")
|
||||
return response.json().get("id") # 获取上传的文件 ID
|
||||
else:
|
||||
print(f"文件上传失败,状态码: {response.status_code}")
|
||||
return None
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
return None
|
||||
|
||||
def run_workflow(file_id, user, response_mode="blocking"):
|
||||
workflow_url = "https://api.dify.ai/v1/workflows/run"
|
||||
headers = {
|
||||
"Authorization": "Bearer app-xxxxxxxxx",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
data = {
|
||||
"inputs": {
|
||||
"orig_mail": {
|
||||
"transfer_method": "local_file",
|
||||
"upload_file_id": file_id,
|
||||
"type": "document"
|
||||
}
|
||||
},
|
||||
"response_mode": response_mode,
|
||||
"user": user
|
||||
}
|
||||
|
||||
try:
|
||||
print("运行工作流...")
|
||||
response = requests.post(workflow_url, headers=headers, json=data)
|
||||
if response.status_code == 200:
|
||||
print("工作流执行成功")
|
||||
return response.json()
|
||||
else:
|
||||
print(f"工作流执行失败,状态码: {response.status_code}")
|
||||
return {"status": "error", "message": f"Failed to execute workflow, status code: {response.status_code}"}
|
||||
except Exception as e:
|
||||
print(f"发生错误: {str(e)}")
|
||||
return {"status": "error", "message": str(e)}
|
||||
|
||||
# 使用示例
|
||||
file_path = "{your_file_path}"
|
||||
user = "difyuser"
|
||||
|
||||
# 上传文件
|
||||
file_id = upload_file(file_path, user)
|
||||
if file_id:
|
||||
# 文件上传成功,继续运行工作流
|
||||
result = run_workflow(file_id, user)
|
||||
print(result)
|
||||
else:
|
||||
print("文件上传失败,无法执行工作流")
|
||||
|
||||
}
|
||||
```
|
||||
</CodeGroup>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
|
||||
@ -14,7 +14,7 @@ import type { App } from '@/models/explore'
|
||||
import Category from '@/app/components/explore/category'
|
||||
import AppCard from '@/app/components/explore/app-card'
|
||||
import { fetchAppDetail, fetchAppList } from '@/service/explore'
|
||||
import { importApp } from '@/service/apps'
|
||||
import { importDSL } from '@/service/apps'
|
||||
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
|
||||
import CreateAppModal from '@/app/components/explore/create-app-modal'
|
||||
import AppTypeSelector from '@/app/components/app/type-selector'
|
||||
@ -24,6 +24,7 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
|
||||
import { useAppContext } from '@/context/app-context'
|
||||
import { getRedirection } from '@/utils/app-redirection'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { DSLImportMode } from '@/models/app'
|
||||
|
||||
type AppsProps = {
|
||||
pageType?: PageType
|
||||
@ -127,8 +128,9 @@ const Apps = ({
|
||||
currApp?.app.id as string,
|
||||
)
|
||||
try {
|
||||
const app = await importApp({
|
||||
data: export_data,
|
||||
const app = await importDSL({
|
||||
mode: DSLImportMode.YAML_CONTENT,
|
||||
yaml_content: export_data,
|
||||
name,
|
||||
icon_type,
|
||||
icon,
|
||||
@ -143,7 +145,7 @@ const Apps = ({
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
|
||||
getRedirection(isCurrentWorkspaceEditor, app, push)
|
||||
getRedirection(isCurrentWorkspaceEditor, { id: app.app_id }, push)
|
||||
}
|
||||
catch (e) {
|
||||
Toast.notify({ type: 'error', message: t('app.newApp.appCreateFailed') })
|
||||
|
||||
@ -11,6 +11,7 @@ import cn from '@/utils/classnames'
|
||||
import { fetchInstalledAppList as doFetchInstalledAppList, uninstallApp, updatePinStatus } from '@/service/explore'
|
||||
import ExploreContext from '@/context/explore-context'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints'
|
||||
|
||||
const SelectedDiscoveryIcon = () => (
|
||||
@ -89,6 +90,7 @@ const SideBar: FC<IExploreSideBarProps> = ({
|
||||
fetchInstalledAppList()
|
||||
}, [controlUpdateInstalledApps])
|
||||
|
||||
const pinnedAppsCount = installedApps.filter(({ is_pinned }) => is_pinned).length
|
||||
return (
|
||||
<div className='w-fit sm:w-[216px] shrink-0 pt-6 px-4 border-gray-200 cursor-pointer'>
|
||||
<div>
|
||||
@ -109,10 +111,9 @@ const SideBar: FC<IExploreSideBarProps> = ({
|
||||
height: 'calc(100vh - 250px)',
|
||||
}}
|
||||
>
|
||||
{installedApps.map(({ id, is_pinned, uninstallable, app: { name, icon_type, icon, icon_url, icon_background } }) => {
|
||||
return (
|
||||
{installedApps.map(({ id, is_pinned, uninstallable, app: { name, icon_type, icon, icon_url, icon_background } }, index) => (
|
||||
<React.Fragment key={id}>
|
||||
<Item
|
||||
key={id}
|
||||
isMobile={isMobile}
|
||||
name={name}
|
||||
icon_type={icon_type}
|
||||
@ -129,8 +130,9 @@ const SideBar: FC<IExploreSideBarProps> = ({
|
||||
setShowConfirm(true)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
{index === pinnedAppsCount - 1 && index !== installedApps.length - 1 && <Divider />}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -34,13 +34,12 @@ const MembersPage = () => {
|
||||
}
|
||||
const { locale } = useContext(I18n)
|
||||
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { userProfile, currentWorkspace, isCurrentWorkspaceOwner, isCurrentWorkspaceManager } = useAppContext()
|
||||
const { data, mutate } = useSWR({ url: '/workspaces/current/members' }, fetchMembers)
|
||||
const [inviteModalVisible, setInviteModalVisible] = useState(false)
|
||||
const [invitationResults, setInvitationResults] = useState<InvitationResult[]>([])
|
||||
const [invitedModalVisible, setInvitedModalVisible] = useState(false)
|
||||
const accounts = data?.accounts || []
|
||||
const owner = accounts.filter(account => account.role === 'owner')?.[0]?.email === userProfile.email
|
||||
const { plan, enableBilling } = useProviderContext()
|
||||
const isNotUnlimitedMemberPlan = enableBilling && plan.type !== Plan.team && plan.type !== Plan.enterprise
|
||||
const isMemberFull = enableBilling && isNotUnlimitedMemberPlan && accounts.length >= plan.total.teamMembers
|
||||
@ -109,8 +108,8 @@ const MembersPage = () => {
|
||||
<div className='shrink-0 flex items-center w-[104px] py-2 text-[13px] text-gray-700'>{dayjs(Number((account.last_active_at || account.created_at)) * 1000).locale(locale === 'zh-Hans' ? 'zh-cn' : 'en').fromNow()}</div>
|
||||
<div className='shrink-0 w-[96px] flex items-center'>
|
||||
{
|
||||
(owner && account.role !== 'owner')
|
||||
? <Operation member={account} onOperate={mutate} />
|
||||
((isCurrentWorkspaceOwner && account.role !== 'owner') || (isCurrentWorkspaceManager && !['owner', 'admin'].includes(account.role)))
|
||||
? <Operation member={account} operatorRole={currentWorkspace.role} onOperate={mutate} />
|
||||
: <div className='px-3 text-[13px] text-gray-700'>{RoleMap[account.role] || RoleMap.normal}</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@ -26,11 +26,13 @@ const itemDescClassName = `
|
||||
|
||||
type IOperationProps = {
|
||||
member: Member
|
||||
operatorRole: string
|
||||
onOperate: () => void
|
||||
}
|
||||
|
||||
const Operation = ({
|
||||
member,
|
||||
operatorRole,
|
||||
onOperate,
|
||||
}: IOperationProps) => {
|
||||
const { t } = useTranslation()
|
||||
@ -43,11 +45,20 @@ const Operation = ({
|
||||
dataset_operator: t('common.members.datasetOperator'),
|
||||
}
|
||||
const roleList = useMemo(() => {
|
||||
return [
|
||||
...['admin', 'editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}, [datasetOperatorEnabled])
|
||||
if (operatorRole === 'owner') {
|
||||
return [
|
||||
...['admin', 'editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}
|
||||
if (operatorRole === 'admin') {
|
||||
return [
|
||||
...['editor', 'normal'],
|
||||
...(datasetOperatorEnabled ? ['dataset_operator'] : []),
|
||||
]
|
||||
}
|
||||
return []
|
||||
}, [operatorRole, datasetOperatorEnabled])
|
||||
const { notify } = useContext(ToastContext)
|
||||
const toHump = (name: string) => name.replace(/_(\w)/g, (all, letter) => letter.toUpperCase())
|
||||
const handleDeleteMemberOrCancelInvitation = async () => {
|
||||
|
||||
@ -52,6 +52,9 @@ export enum ModelFeatureEnum {
|
||||
multiToolCall = 'multi-tool-call',
|
||||
agentThought = 'agent-thought',
|
||||
vision = 'vision',
|
||||
video = 'video',
|
||||
document = 'document',
|
||||
audio = 'audio',
|
||||
}
|
||||
|
||||
export enum ModelFeatureTextEnum {
|
||||
@ -59,6 +62,9 @@ export enum ModelFeatureTextEnum {
|
||||
multiToolCall = 'Multi Tool Call',
|
||||
agentThought = 'Agent Thought',
|
||||
vision = 'Vision',
|
||||
video = 'Video',
|
||||
document = 'Document',
|
||||
audio = 'Audio',
|
||||
}
|
||||
|
||||
export enum ModelStatusEnum {
|
||||
|
||||
@ -22,6 +22,7 @@ export const useWorkflowTemplate = () => {
|
||||
...nodesInitialData.llm,
|
||||
memory: {
|
||||
window: { enabled: false, size: 10 },
|
||||
query_prompt_template: '{{#sys.query#}}',
|
||||
},
|
||||
selected: true,
|
||||
},
|
||||
|
||||
@ -16,6 +16,7 @@ import { InputVarType, NodeRunningStatus } from '@/app/components/workflow/types
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { TransferMethod } from '@/types/app'
|
||||
import { getProcessedFiles } from '@/app/components/base/file-uploader/utils'
|
||||
|
||||
const i18nPrefix = 'workflow.singleRun'
|
||||
|
||||
@ -39,6 +40,11 @@ function formatValue(value: string | any, type: InputVarType) {
|
||||
return JSON.parse(item)
|
||||
})
|
||||
}
|
||||
if (type === InputVarType.multiFiles)
|
||||
return getProcessedFiles(value)
|
||||
|
||||
if (type === InputVarType.singleFile)
|
||||
return getProcessedFiles([value])[0]
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
@ -160,6 +160,7 @@ const CodeEditor: FC<Props> = ({
|
||||
hideSearch
|
||||
vars={availableVars}
|
||||
onChange={handleSelectVar}
|
||||
isSupportFileVar={false}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -128,7 +128,7 @@ const PanelOperatorPopup = ({
|
||||
className='flex items-center justify-between px-3 h-8 text-sm text-gray-700 rounded-lg cursor-pointer hover:bg-gray-50'
|
||||
onClick={() => {
|
||||
onClosePopup()
|
||||
handleNodesCopy()
|
||||
handleNodesCopy(id)
|
||||
}}
|
||||
>
|
||||
{t('workflow.common.copy')}
|
||||
|
||||
@ -18,6 +18,7 @@ type Props = {
|
||||
isSupportConstantValue?: boolean
|
||||
onlyLeafNodeVar?: boolean
|
||||
filterVar?: (payload: Var, valueSelector: ValueSelector) => boolean
|
||||
isSupportFileVar?: boolean
|
||||
}
|
||||
|
||||
const VarList: FC<Props> = ({
|
||||
@ -29,6 +30,7 @@ const VarList: FC<Props> = ({
|
||||
isSupportConstantValue,
|
||||
onlyLeafNodeVar,
|
||||
filterVar,
|
||||
isSupportFileVar = true,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
@ -94,6 +96,7 @@ const VarList: FC<Props> = ({
|
||||
defaultVarKindType={item.variable_type}
|
||||
onlyLeafNodeVar={onlyLeafNodeVar}
|
||||
filterVar={filterVar}
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
/>
|
||||
{!readonly && (
|
||||
<RemoveButton
|
||||
|
||||
@ -59,6 +59,7 @@ type Props = {
|
||||
isInTable?: boolean
|
||||
onRemove?: () => void
|
||||
typePlaceHolder?: string
|
||||
isSupportFileVar?: boolean
|
||||
}
|
||||
|
||||
const VarReferencePicker: FC<Props> = ({
|
||||
@ -81,6 +82,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
isInTable,
|
||||
onRemove,
|
||||
typePlaceHolder,
|
||||
isSupportFileVar = true,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const store = useStoreApi()
|
||||
@ -382,6 +384,7 @@ const VarReferencePicker: FC<Props> = ({
|
||||
vars={outputVars}
|
||||
onChange={handleVarReferenceChange}
|
||||
itemWidth={isAddBtnTrigger ? 260 : triggerWidth}
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
/>
|
||||
)}
|
||||
</PortalToFollowElemContent>
|
||||
|
||||
@ -8,11 +8,13 @@ type Props = {
|
||||
vars: NodeOutPutVar[]
|
||||
onChange: (value: ValueSelector, varDetail: Var) => void
|
||||
itemWidth?: number
|
||||
isSupportFileVar?: boolean
|
||||
}
|
||||
const VarReferencePopup: FC<Props> = ({
|
||||
vars,
|
||||
onChange,
|
||||
itemWidth,
|
||||
isSupportFileVar = true,
|
||||
}) => {
|
||||
// max-h-[300px] overflow-y-auto todo: use portal to handle long list
|
||||
return (
|
||||
@ -24,7 +26,7 @@ const VarReferencePopup: FC<Props> = ({
|
||||
vars={vars}
|
||||
onChange={onChange}
|
||||
itemWidth={itemWidth}
|
||||
isSupportFileVar
|
||||
isSupportFileVar={isSupportFileVar}
|
||||
/>
|
||||
</div >
|
||||
)
|
||||
|
||||
@ -89,6 +89,7 @@ const Panel: FC<NodePanelProps<CodeNodeType>> = ({
|
||||
list={inputs.variables}
|
||||
onChange={handleVarListChange}
|
||||
filterVar={filterVar}
|
||||
isSupportFileVar={false}
|
||||
/>
|
||||
</Field>
|
||||
<Split />
|
||||
|
||||
154
web/app/components/workflow/nodes/http/components/curl-panel.tsx
Normal file
154
web/app/components/workflow/nodes/http/components/curl-panel.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { BodyType, type HttpNodeType, Method } from '../types'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { useNodesInteractions } from '@/app/components/workflow/hooks'
|
||||
|
||||
type Props = {
|
||||
nodeId: string
|
||||
isShow: boolean
|
||||
onHide: () => void
|
||||
handleCurlImport: (node: HttpNodeType) => void
|
||||
}
|
||||
|
||||
const parseCurl = (curlCommand: string): { node: HttpNodeType | null; error: string | null } => {
|
||||
if (!curlCommand.trim().toLowerCase().startsWith('curl'))
|
||||
return { node: null, error: 'Invalid cURL command. Command must start with "curl".' }
|
||||
|
||||
const node: Partial<HttpNodeType> = {
|
||||
title: 'HTTP Request',
|
||||
desc: 'Imported from cURL',
|
||||
method: Method.get,
|
||||
url: '',
|
||||
headers: '',
|
||||
params: '',
|
||||
body: { type: BodyType.none, data: '' },
|
||||
}
|
||||
const args = curlCommand.match(/(?:[^\s"']+|"[^"]*"|'[^']*')+/g) || []
|
||||
|
||||
for (let i = 1; i < args.length; i++) {
|
||||
const arg = args[i].replace(/^['"]|['"]$/g, '')
|
||||
switch (arg) {
|
||||
case '-X':
|
||||
case '--request':
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing HTTP method after -X or --request.' }
|
||||
node.method = (args[++i].replace(/^['"]|['"]$/g, '') as Method) || Method.get
|
||||
break
|
||||
case '-H':
|
||||
case '--header':
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing header value after -H or --header.' }
|
||||
node.headers += (node.headers ? '\n' : '') + args[++i].replace(/^['"]|['"]$/g, '')
|
||||
break
|
||||
case '-d':
|
||||
case '--data':
|
||||
case '--data-raw':
|
||||
case '--data-binary':
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing data value after -d, --data, --data-raw, or --data-binary.' }
|
||||
node.body = { type: BodyType.rawText, data: args[++i].replace(/^['"]|['"]$/g, '') }
|
||||
break
|
||||
case '-F':
|
||||
case '--form': {
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing form data after -F or --form.' }
|
||||
if (node.body?.type !== BodyType.formData)
|
||||
node.body = { type: BodyType.formData, data: '' }
|
||||
const formData = args[++i].replace(/^['"]|['"]$/g, '')
|
||||
const [key, ...valueParts] = formData.split('=')
|
||||
if (!key)
|
||||
return { node: null, error: 'Invalid form data format.' }
|
||||
let value = valueParts.join('=')
|
||||
|
||||
// To support command like `curl -F "file=@/path/to/file;type=application/zip"`
|
||||
// the `;type=application/zip` should translate to `Content-Type: application/zip`
|
||||
const typeMatch = value.match(/^(.+?);type=(.+)$/)
|
||||
if (typeMatch) {
|
||||
const [, actualValue, mimeType] = typeMatch
|
||||
value = actualValue
|
||||
node.headers += `${node.headers ? '\n' : ''}Content-Type: ${mimeType}`
|
||||
}
|
||||
|
||||
node.body.data += `${node.body.data ? '\n' : ''}${key}:${value}`
|
||||
break
|
||||
}
|
||||
case '--json':
|
||||
if (i + 1 >= args.length)
|
||||
return { node: null, error: 'Missing JSON data after --json.' }
|
||||
node.body = { type: BodyType.json, data: args[++i].replace(/^['"]|['"]$/g, '') }
|
||||
break
|
||||
default:
|
||||
if (arg.startsWith('http') && !node.url)
|
||||
node.url = arg
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (!node.url)
|
||||
return { node: null, error: 'Missing URL or url not start with http.' }
|
||||
|
||||
// Extract query params from URL
|
||||
const urlParts = node.url?.split('?') || []
|
||||
if (urlParts.length > 1) {
|
||||
node.url = urlParts[0]
|
||||
node.params = urlParts[1].replace(/&/g, '\n').replace(/=/g, ': ')
|
||||
}
|
||||
|
||||
return { node: node as HttpNodeType, error: null }
|
||||
}
|
||||
|
||||
const CurlPanel: FC<Props> = ({ nodeId, isShow, onHide, handleCurlImport }) => {
|
||||
const [inputString, setInputString] = useState('')
|
||||
const { handleNodeSelect } = useNodesInteractions()
|
||||
const { t } = useTranslation()
|
||||
|
||||
const handleSave = useCallback(() => {
|
||||
const { node, error } = parseCurl(inputString)
|
||||
if (error) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error,
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!node)
|
||||
return
|
||||
|
||||
onHide()
|
||||
handleCurlImport(node)
|
||||
// Close the panel then open it again to make the panel re-render
|
||||
handleNodeSelect(nodeId, true)
|
||||
setTimeout(() => {
|
||||
handleNodeSelect(nodeId)
|
||||
}, 0)
|
||||
}, [onHide, nodeId, inputString, handleNodeSelect, handleCurlImport])
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t('workflow.nodes.http.curl.title')}
|
||||
isShow={isShow}
|
||||
onClose={onHide}
|
||||
className='!w-[400px] !max-w-[400px] !p-4'
|
||||
>
|
||||
<div>
|
||||
<textarea
|
||||
value={inputString}
|
||||
className='w-full my-3 p-3 text-sm text-gray-900 border-0 rounded-lg grow bg-gray-100 focus:outline-none focus:ring-1 focus:ring-inset focus:ring-gray-200 h-40'
|
||||
onChange={e => setInputString(e.target.value)}
|
||||
placeholder={t('workflow.nodes.http.curl.placeholder')!}
|
||||
/>
|
||||
</div>
|
||||
<div className='mt-4 flex justify-end space-x-2'>
|
||||
<Button className='!w-[95px]' onClick={onHide} >{t('common.operation.cancel')}</Button>
|
||||
<Button className='!w-[95px]' variant='primary' onClick={handleSave} > {t('common.operation.save')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(CurlPanel)
|
||||
@ -8,11 +8,13 @@ import EditBody from './components/edit-body'
|
||||
import AuthorizationModal from './components/authorization'
|
||||
import type { HttpNodeType } from './types'
|
||||
import Timeout from './components/timeout'
|
||||
import CurlPanel from './components/curl-panel'
|
||||
import cn from '@/utils/classnames'
|
||||
import Field from '@/app/components/workflow/nodes/_base/components/field'
|
||||
import Split from '@/app/components/workflow/nodes/_base/components/split'
|
||||
import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars'
|
||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { FileArrow01 } from '@/app/components/base/icons/src/vender/line/files'
|
||||
import type { NodePanelProps } from '@/app/components/workflow/types'
|
||||
import BeforeRunForm from '@/app/components/workflow/nodes/_base/components/before-run-form'
|
||||
import ResultPanel from '@/app/components/workflow/run/result-panel'
|
||||
@ -53,6 +55,10 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({
|
||||
inputVarValues,
|
||||
setInputVarValues,
|
||||
runResult,
|
||||
isShowCurlPanel,
|
||||
showCurlPanel,
|
||||
hideCurlPanel,
|
||||
handleCurlImport,
|
||||
} = useConfig(id, data)
|
||||
// To prevent prompt editor in body not update data.
|
||||
if (!isDataReady)
|
||||
@ -64,14 +70,25 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({
|
||||
<Field
|
||||
title={t(`${i18nPrefix}.api`)}
|
||||
operations={
|
||||
<div
|
||||
onClick={showAuthorization}
|
||||
className={cn(!readOnly && 'cursor-pointer hover:bg-gray-50', 'flex items-center h-6 space-x-1 px-2 rounded-md ')}
|
||||
>
|
||||
{!readOnly && <Settings01 className='w-3 h-3 text-gray-500' />}
|
||||
<div className='text-xs font-medium text-gray-500'>
|
||||
{t(`${i18nPrefix}.authorization.authorization`)}
|
||||
<span className='ml-1 text-gray-700'>{t(`${i18nPrefix}.authorization.${inputs.authorization.type}`)}</span>
|
||||
<div className='flex'>
|
||||
<div
|
||||
onClick={showAuthorization}
|
||||
className={cn(!readOnly && 'cursor-pointer hover:bg-gray-50', 'flex items-center h-6 space-x-1 px-2 rounded-md ')}
|
||||
>
|
||||
{!readOnly && <Settings01 className='w-3 h-3 text-gray-500' />}
|
||||
<div className='text-xs font-medium text-gray-500'>
|
||||
{t(`${i18nPrefix}.authorization.authorization`)}
|
||||
<span className='ml-1 text-gray-700'>{t(`${i18nPrefix}.authorization.${inputs.authorization.type}`)}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
onClick={showCurlPanel}
|
||||
className={cn(!readOnly && 'cursor-pointer hover:bg-gray-50', 'flex items-center h-6 space-x-1 px-2 rounded-md ')}
|
||||
>
|
||||
{!readOnly && <FileArrow01 className='w-3 h-3 text-gray-500' />}
|
||||
<div className='text-xs font-medium text-gray-500'>
|
||||
{t(`${i18nPrefix}.curl.title`)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -180,7 +197,15 @@ const Panel: FC<NodePanelProps<HttpNodeType>> = ({
|
||||
result={<ResultPanel {...runResult} showSteps={false} />}
|
||||
/>
|
||||
)}
|
||||
</div >
|
||||
{(isShowCurlPanel && !readOnly) && (
|
||||
<CurlPanel
|
||||
nodeId={id}
|
||||
isShow
|
||||
onHide={hideCurlPanel}
|
||||
handleCurlImport={handleCurlImport}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -164,6 +164,23 @@ const useConfig = (id: string, payload: HttpNodeType) => {
|
||||
setRunInputData(newPayload)
|
||||
}, [setRunInputData])
|
||||
|
||||
// curl import panel
|
||||
const [isShowCurlPanel, {
|
||||
setTrue: showCurlPanel,
|
||||
setFalse: hideCurlPanel,
|
||||
}] = useBoolean(false)
|
||||
|
||||
const handleCurlImport = useCallback((newNode: HttpNodeType) => {
|
||||
const newInputs = produce(inputs, (draft: HttpNodeType) => {
|
||||
draft.method = newNode.method
|
||||
draft.url = newNode.url
|
||||
draft.headers = newNode.headers
|
||||
draft.params = newNode.params
|
||||
draft.body = newNode.body
|
||||
})
|
||||
setInputs(newInputs)
|
||||
}, [inputs, setInputs])
|
||||
|
||||
return {
|
||||
readOnly,
|
||||
isDataReady,
|
||||
@ -203,6 +220,11 @@ const useConfig = (id: string, payload: HttpNodeType) => {
|
||||
inputVarValues,
|
||||
setInputVarValues,
|
||||
runResult,
|
||||
// curl import
|
||||
isShowCurlPanel,
|
||||
showCurlPanel,
|
||||
hideCurlPanel,
|
||||
handleCurlImport,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -144,6 +144,7 @@ const ConfigPromptItem: FC<Props> = ({
|
||||
onEditionTypeChange={onEditionTypeChange}
|
||||
varList={varList}
|
||||
handleAddVariable={handleAddVariable}
|
||||
isSupportFileVar
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
@ -67,6 +67,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
handleStop,
|
||||
varInputs,
|
||||
runResult,
|
||||
filterJinjia2InputVar,
|
||||
} = useConfig(id, data)
|
||||
|
||||
const model = inputs.model
|
||||
@ -194,7 +195,8 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
list={inputs.prompt_config?.jinja2_variables || []}
|
||||
onChange={handleVarListChange}
|
||||
onVarNameChange={handleVarNameChange}
|
||||
filterVar={filterVar}
|
||||
filterVar={filterJinjia2InputVar}
|
||||
isSupportFileVar={false}
|
||||
/>
|
||||
</Field>
|
||||
)}
|
||||
@ -233,6 +235,7 @@ const Panel: FC<NodePanelProps<LLMNodeType>> = ({
|
||||
hasSetBlockStatus={hasSetBlockStatus}
|
||||
nodesOutputVars={availableVars}
|
||||
availableNodes={availableNodesWithParent}
|
||||
isSupportFileVar
|
||||
/>
|
||||
|
||||
{inputs.memory.query_prompt_template && !inputs.memory.query_prompt_template.includes('{{#sys.query#}}') && (
|
||||
|
||||
@ -278,11 +278,15 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
}, [inputs, setInputs])
|
||||
|
||||
const filterInputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const filterJinjia2InputVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const filterMemoryPromptVar = useCallback((varPayload: Var) => {
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber].includes(varPayload.type)
|
||||
return [VarType.arrayObject, VarType.array, VarType.number, VarType.string, VarType.secret, VarType.arrayString, VarType.arrayNumber, VarType.file, VarType.arrayFile].includes(varPayload.type)
|
||||
}, [])
|
||||
|
||||
const {
|
||||
@ -406,6 +410,7 @@ const useConfig = (id: string, payload: LLMNodeType) => {
|
||||
handleRun,
|
||||
handleStop,
|
||||
runResult,
|
||||
filterJinjia2InputVar,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,6 +64,7 @@ const Panel: FC<NodePanelProps<TemplateTransformNodeType>> = ({
|
||||
onChange={handleVarListChange}
|
||||
onVarNameChange={handleVarNameChange}
|
||||
filterVar={filterVar}
|
||||
isSupportFileVar={false}
|
||||
/>
|
||||
</Field>
|
||||
<Split />
|
||||
|
||||
@ -46,6 +46,8 @@ const InputVarList: FC<Props> = ({
|
||||
const paramType = (type: string) => {
|
||||
if (type === FormTypeEnum.textNumber)
|
||||
return 'Number'
|
||||
else if (type === FormTypeEnum.file)
|
||||
return 'File'
|
||||
else if (type === FormTypeEnum.files)
|
||||
return 'Files'
|
||||
else if (type === FormTypeEnum.select)
|
||||
|
||||
@ -81,7 +81,6 @@ const NoteNode = ({
|
||||
nodeData={data}
|
||||
icon={<Icon />}
|
||||
minWidth={240}
|
||||
maxWidth={640}
|
||||
minHeight={88}
|
||||
/>
|
||||
<div className='shrink-0 h-2 opacity-50 rounded-t-md' style={{ background: THEME_MAP[theme].title }}></div>
|
||||
|
||||
@ -15,4 +15,8 @@
|
||||
#workflow-container .react-flow__selection {
|
||||
border: 1px solid #528BFF;
|
||||
background: rgba(21, 94, 239, 0.05);
|
||||
}
|
||||
|
||||
#workflow-container .react-flow__node-custom-note {
|
||||
z-index: -1000 !important;
|
||||
}
|
||||
@ -10,8 +10,9 @@ import {
|
||||
import { useContext } from 'use-context-selector'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiAlertLine,
|
||||
RiAlertFill,
|
||||
RiCloseLine,
|
||||
RiFileDownloadLine,
|
||||
} from '@remixicon/react'
|
||||
import { WORKFLOW_DATA_UPDATE } from './constants'
|
||||
import {
|
||||
@ -21,11 +22,19 @@ import {
|
||||
initialEdges,
|
||||
initialNodes,
|
||||
} from './utils'
|
||||
import {
|
||||
importDSL,
|
||||
importDSLConfirm,
|
||||
} from '@/service/apps'
|
||||
import { fetchWorkflowDraft } from '@/service/workflow'
|
||||
import {
|
||||
DSLImportMode,
|
||||
DSLImportStatus,
|
||||
} from '@/models/app'
|
||||
import Uploader from '@/app/components/app/create-from-dsl-modal/uploader'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
import { updateWorkflowDraftFromDSL } from '@/service/workflow'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { FILE_EXTS } from '@/app/components/base/prompt-editor/constants'
|
||||
@ -48,6 +57,10 @@ const UpdateDSLModal = ({
|
||||
const [fileContent, setFileContent] = useState<string>()
|
||||
const [loading, setLoading] = useState(false)
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [show, setShow] = useState(true)
|
||||
const [showErrorModal, setShowErrorModal] = useState(false)
|
||||
const [versions, setVersions] = useState<{ importedVersion: string; systemVersion: string }>()
|
||||
const [importId, setImportId] = useState<string>()
|
||||
|
||||
const readFile = (file: File) => {
|
||||
const reader = new FileReader()
|
||||
@ -66,6 +79,51 @@ const UpdateDSLModal = ({
|
||||
setFileContent('')
|
||||
}
|
||||
|
||||
const handleWorkflowUpdate = async (app_id: string) => {
|
||||
const {
|
||||
graph,
|
||||
features,
|
||||
hash,
|
||||
} = await fetchWorkflowDraft(`/apps/${app_id}/workflows/draft`)
|
||||
|
||||
const { nodes, edges, viewport } = graph
|
||||
const newFeatures = {
|
||||
file: {
|
||||
image: {
|
||||
enabled: !!features.file_upload?.image?.enabled,
|
||||
number_limits: features.file_upload?.image?.number_limits || 3,
|
||||
transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
},
|
||||
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
||||
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||
allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
|
||||
allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
|
||||
},
|
||||
opening: {
|
||||
enabled: !!features.opening_statement,
|
||||
opening_statement: features.opening_statement,
|
||||
suggested_questions: features.suggested_questions,
|
||||
},
|
||||
suggested: features.suggested_questions_after_answer || { enabled: false },
|
||||
speech2text: features.speech_to_text || { enabled: false },
|
||||
text2speech: features.text_to_speech || { enabled: false },
|
||||
citation: features.retriever_resource || { enabled: false },
|
||||
moderation: features.sensitive_word_avoidance || { enabled: false },
|
||||
}
|
||||
|
||||
eventEmitter?.emit({
|
||||
type: WORKFLOW_DATA_UPDATE,
|
||||
payload: {
|
||||
nodes: initialNodes(nodes, edges),
|
||||
edges: initialEdges(edges, nodes),
|
||||
viewport,
|
||||
features: newFeatures,
|
||||
hash,
|
||||
},
|
||||
} as any)
|
||||
}
|
||||
|
||||
const isCreatingRef = useRef(false)
|
||||
const handleImport: MouseEventHandler = useCallback(async () => {
|
||||
if (isCreatingRef.current)
|
||||
@ -76,51 +134,39 @@ const UpdateDSLModal = ({
|
||||
try {
|
||||
if (appDetail && fileContent) {
|
||||
setLoading(true)
|
||||
const {
|
||||
graph,
|
||||
features,
|
||||
hash,
|
||||
} = await updateWorkflowDraftFromDSL(appDetail.id, fileContent)
|
||||
const { nodes, edges, viewport } = graph
|
||||
const newFeatures = {
|
||||
file: {
|
||||
image: {
|
||||
enabled: !!features.file_upload?.image?.enabled,
|
||||
number_limits: features.file_upload?.image?.number_limits || 3,
|
||||
transfer_methods: features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
},
|
||||
enabled: !!(features.file_upload?.enabled || features.file_upload?.image?.enabled),
|
||||
allowed_file_types: features.file_upload?.allowed_file_types || [SupportUploadFileTypes.image],
|
||||
allowed_file_extensions: features.file_upload?.allowed_file_extensions || FILE_EXTS[SupportUploadFileTypes.image].map(ext => `.${ext}`),
|
||||
allowed_file_upload_methods: features.file_upload?.allowed_file_upload_methods || features.file_upload?.image?.transfer_methods || ['local_file', 'remote_url'],
|
||||
number_limits: features.file_upload?.number_limits || features.file_upload?.image?.number_limits || 3,
|
||||
},
|
||||
opening: {
|
||||
enabled: !!features.opening_statement,
|
||||
opening_statement: features.opening_statement,
|
||||
suggested_questions: features.suggested_questions,
|
||||
},
|
||||
suggested: features.suggested_questions_after_answer || { enabled: false },
|
||||
speech2text: features.speech_to_text || { enabled: false },
|
||||
text2speech: features.text_to_speech || { enabled: false },
|
||||
citation: features.retriever_resource || { enabled: false },
|
||||
moderation: features.sensitive_word_avoidance || { enabled: false },
|
||||
const response = await importDSL({ mode: DSLImportMode.YAML_CONTENT, yaml_content: fileContent, app_id: appDetail.id })
|
||||
const { id, status, app_id, imported_dsl_version, current_dsl_version } = response
|
||||
if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) {
|
||||
if (!app_id) {
|
||||
notify({ type: 'error', message: t('workflow.common.importFailure') })
|
||||
return
|
||||
}
|
||||
handleWorkflowUpdate(app_id)
|
||||
if (onImport)
|
||||
onImport()
|
||||
notify({
|
||||
type: status === DSLImportStatus.COMPLETED ? 'success' : 'warning',
|
||||
message: t(status === DSLImportStatus.COMPLETED ? 'workflow.common.importSuccess' : 'workflow.common.importWarning'),
|
||||
children: status === DSLImportStatus.COMPLETED_WITH_WARNINGS && t('workflow.common.importWarningDetails'),
|
||||
})
|
||||
setLoading(false)
|
||||
onCancel()
|
||||
}
|
||||
else if (status === DSLImportStatus.PENDING) {
|
||||
setShow(false)
|
||||
setTimeout(() => {
|
||||
setShowErrorModal(true)
|
||||
}, 300)
|
||||
setVersions({
|
||||
importedVersion: imported_dsl_version ?? '',
|
||||
systemVersion: current_dsl_version ?? '',
|
||||
})
|
||||
setImportId(id)
|
||||
}
|
||||
else {
|
||||
setLoading(false)
|
||||
notify({ type: 'error', message: t('workflow.common.importFailure') })
|
||||
}
|
||||
eventEmitter?.emit({
|
||||
type: WORKFLOW_DATA_UPDATE,
|
||||
payload: {
|
||||
nodes: initialNodes(nodes, edges),
|
||||
edges: initialEdges(edges, nodes),
|
||||
viewport,
|
||||
features: newFeatures,
|
||||
hash,
|
||||
},
|
||||
} as any)
|
||||
if (onImport)
|
||||
onImport()
|
||||
notify({ type: 'success', message: t('workflow.common.importSuccess') })
|
||||
setLoading(false)
|
||||
onCancel()
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
@ -130,52 +176,119 @@ const UpdateDSLModal = ({
|
||||
isCreatingRef.current = false
|
||||
}, [currentFile, fileContent, onCancel, notify, t, eventEmitter, appDetail, onImport])
|
||||
|
||||
const onUpdateDSLConfirm: MouseEventHandler = async () => {
|
||||
try {
|
||||
if (!importId)
|
||||
return
|
||||
const response = await importDSLConfirm({
|
||||
import_id: importId,
|
||||
})
|
||||
|
||||
const { status, app_id } = response
|
||||
|
||||
if (status === DSLImportStatus.COMPLETED) {
|
||||
if (!app_id) {
|
||||
notify({ type: 'error', message: t('workflow.common.importFailure') })
|
||||
return
|
||||
}
|
||||
handleWorkflowUpdate(app_id)
|
||||
if (onImport)
|
||||
onImport()
|
||||
notify({ type: 'success', message: t('workflow.common.importSuccess') })
|
||||
setLoading(false)
|
||||
onCancel()
|
||||
}
|
||||
else if (status === DSLImportStatus.FAILED) {
|
||||
setLoading(false)
|
||||
notify({ type: 'error', message: t('workflow.common.importFailure') })
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
setLoading(false)
|
||||
notify({ type: 'error', message: t('workflow.common.importFailure') })
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className='p-6 w-[520px] rounded-2xl'
|
||||
isShow={true}
|
||||
onClose={() => {}}
|
||||
>
|
||||
<div className='flex items-center justify-between mb-6'>
|
||||
<div className='text-2xl font-semibold text-[#101828]'>{t('workflow.common.importDSL')}</div>
|
||||
<div className='flex items-center justify-center w-[22px] h-[22px] cursor-pointer' onClick={onCancel}>
|
||||
<RiCloseLine className='w-5 h-5 text-gray-500' />
|
||||
<>
|
||||
<Modal
|
||||
className='p-6 w-[520px] rounded-2xl'
|
||||
isShow={show}
|
||||
onClose={onCancel}
|
||||
>
|
||||
<div className='flex items-center justify-between mb-3'>
|
||||
<div className='title-2xl-semi-bold text-text-primary'>{t('workflow.common.importDSL')}</div>
|
||||
<div className='flex items-center justify-center w-[22px] h-[22px] cursor-pointer' onClick={onCancel}>
|
||||
<RiCloseLine className='w-[18px] h-[18px] text-text-tertiary' />
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex relative p-2 mb-2 gap-0.5 flex-grow rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur shadow-xs overflow-hidden'>
|
||||
<div className='absolute top-0 left-0 w-full h-full opacity-40 bg-[linear-gradient(92deg,rgba(247,144,9,0.25)_0%,rgba(255,255,255,0.00)_100%)]' />
|
||||
<div className='flex p-1 justify-center items-start'>
|
||||
<RiAlertFill className='w-4 h-4 flex-shrink-0 text-text-warning-secondary' />
|
||||
</div>
|
||||
<div className='flex py-1 flex-col items-start gap-0.5 flex-grow'>
|
||||
<div className='text-text-primary system-xs-medium whitespace-pre-line'>{t('workflow.common.importDSLTip')}</div>
|
||||
<div className='flex pt-1 pb-0.5 items-start gap-1 self-stretch'>
|
||||
<Button
|
||||
size='small'
|
||||
variant='secondary'
|
||||
className='z-[1000]'
|
||||
onClick={onBackup}
|
||||
>
|
||||
<RiFileDownloadLine className='w-3.5 h-3.5 text-components-button-secondary-text' />
|
||||
<div className='flex px-[3px] justify-center items-center gap-1'>
|
||||
{t('workflow.common.backupCurrentDraft')}
|
||||
</div>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex mb-4 px-4 py-3 bg-[#FFFAEB] rounded-xl border border-[#FEDF89]'>
|
||||
<RiAlertLine className='shrink-0 mt-0.5 mr-2 w-4 h-4 text-[#F79009]' />
|
||||
<div>
|
||||
<div className='mb-2 text-sm font-medium text-[#354052]'>{t('workflow.common.importDSLTip')}</div>
|
||||
<div className='pt-2 text-text-primary system-md-semibold'>
|
||||
{t('workflow.common.chooseDSL')}
|
||||
</div>
|
||||
<div className='flex w-full py-4 flex-col justify-center items-start gap-4 self-stretch'>
|
||||
<Uploader
|
||||
file={currentFile}
|
||||
updateFile={handleFile}
|
||||
className='!mt-0 w-full'
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex pt-5 gap-2 items-center justify-end self-stretch'>
|
||||
<Button onClick={onCancel}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button
|
||||
variant='secondary-accent'
|
||||
onClick={onBackup}
|
||||
disabled={!currentFile || loading}
|
||||
variant='warning'
|
||||
onClick={handleImport}
|
||||
loading={loading}
|
||||
>
|
||||
{t('workflow.common.backupCurrentDraft')}
|
||||
{t('workflow.common.overwriteAndImport')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className='mb-8'>
|
||||
<div className='mb-1 text-[13px] font-semibold text-[#354052]'>
|
||||
{t('workflow.common.chooseDSL')}
|
||||
</Modal>
|
||||
<Modal
|
||||
isShow={showErrorModal}
|
||||
onClose={() => setShowErrorModal(false)}
|
||||
className='w-[480px]'
|
||||
>
|
||||
<div className='flex pb-4 flex-col items-start gap-2 self-stretch'>
|
||||
<div className='text-text-primary title-2xl-semi-bold'>{t('app.newApp.appCreateDSLErrorTitle')}</div>
|
||||
<div className='flex flex-grow flex-col text-text-secondary system-md-regular'>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart1')}</div>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart2')}</div>
|
||||
<br />
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart3')}<span className='system-md-medium'>{versions?.importedVersion}</span></div>
|
||||
<div>{t('app.newApp.appCreateDSLErrorPart4')}<span className='system-md-medium'>{versions?.systemVersion}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<Uploader
|
||||
file={currentFile}
|
||||
updateFile={handleFile}
|
||||
className='!mt-0'
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-end'>
|
||||
<Button className='mr-2' onClick={onCancel}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button
|
||||
disabled={!currentFile || loading}
|
||||
variant='warning'
|
||||
onClick={handleImport}
|
||||
loading={loading}
|
||||
>
|
||||
{t('workflow.common.overwriteAndImport')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
<div className='flex pt-6 justify-end items-start gap-2 self-stretch'>
|
||||
<Button variant='secondary' onClick={() => setShowErrorModal(false)}>{t('app.newApp.Cancel')}</Button>
|
||||
<Button variant='primary' destructive onClick={onUpdateDSLConfirm}>{t('app.newApp.Confirm')}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,25 +1,10 @@
|
||||
import Script from 'next/script'
|
||||
import Header from './_header'
|
||||
import style from './page.module.css'
|
||||
|
||||
import cn from '@/utils/classnames'
|
||||
import { IS_CE_EDITION } from '@/config'
|
||||
|
||||
export default async function SignInLayout({ children }: any) {
|
||||
return <>
|
||||
{!IS_CE_EDITION && (
|
||||
<>
|
||||
<Script strategy="beforeInteractive" async src={'https://www.googletagmanager.com/gtag/js?id=AW-11217955271'}></Script>
|
||||
<Script
|
||||
id="ga-monitor-register"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: 'window.dataLayer2 = window.dataLayer2 || [];function gtag(){dataLayer2.push(arguments);}gtag(\'js\', new Date());gtag(\'config\', \'AW-11217955271"\');',
|
||||
}}
|
||||
>
|
||||
</Script>
|
||||
</>
|
||||
)}
|
||||
|
||||
<div className={cn(
|
||||
style.background,
|
||||
'flex w-full min-h-screen',
|
||||
|
||||
@ -4,6 +4,8 @@
|
||||
|
||||
@import '../../themes/light.css';
|
||||
@import '../../themes/dark.css';
|
||||
@import "../../themes/manual-light.css";
|
||||
@import "../../themes/manual-dark.css";
|
||||
|
||||
html[data-changing-theme] * {
|
||||
transition: none !important;
|
||||
|
||||
@ -97,6 +97,8 @@ type IDebugConfiguration = {
|
||||
isShowVisionConfig: boolean
|
||||
visionConfig: VisionSettings
|
||||
setVisionConfig: (visionConfig: VisionSettings, noNotice?: boolean) => void
|
||||
isAllowVideoUpload: boolean
|
||||
isShowDocumentConfig: boolean
|
||||
rerankSettingModalOpen: boolean
|
||||
setRerankSettingModalOpen: (rerankSettingModalOpen: boolean) => void
|
||||
}
|
||||
@ -244,6 +246,8 @@ const DebugConfigurationContext = createContext<IDebugConfiguration>({
|
||||
transfer_methods: [TransferMethod.remote_url],
|
||||
},
|
||||
setVisionConfig: () => { },
|
||||
isAllowVideoUpload: false,
|
||||
isShowDocumentConfig: false,
|
||||
rerankSettingModalOpen: false,
|
||||
setRerankSettingModalOpen: () => { },
|
||||
})
|
||||
|
||||
@ -76,6 +76,7 @@ const translation = {
|
||||
requestBody: 'Anfragekörper',
|
||||
pathParams: 'Pfadparameter',
|
||||
query: 'Anfrage',
|
||||
toc: 'Inhalt',
|
||||
},
|
||||
loading: 'Laden',
|
||||
regenerate: 'Erneuern',
|
||||
|
||||
@ -64,6 +64,14 @@ const translation = {
|
||||
agentDescription: 'Erstellen Sie einen intelligenten Agenten, der autonom Werkzeuge auswählen kann, um die Aufgaben zu erledigen',
|
||||
completionDescription: 'Erstellen Sie eine Anwendung, die qualitativ hochwertigen Text auf der Grundlage von Eingabeaufforderungen generiert, z. B. zum Generieren von Artikeln, Zusammenfassungen, Übersetzungen und mehr.',
|
||||
appDescriptionPlaceholder: 'Geben Sie die Beschreibung der App ein',
|
||||
caution: 'Vorsicht',
|
||||
Confirm: 'Bestätigen',
|
||||
appCreateDSLErrorTitle: 'Inkompatibilität der Version',
|
||||
appCreateDSLErrorPart2: 'Möchten Sie fortfahren?',
|
||||
appCreateDSLErrorPart4: 'Systemgestützte DSL-Version:',
|
||||
appCreateDSLErrorPart1: 'Es wurde ein signifikanter Unterschied bei den DSL-Versionen festgestellt. Das Erzwingen des Imports kann zu Fehlfunktionen der Anwendung führen.',
|
||||
appCreateDSLErrorPart3: 'Aktuelle DSL-Version der Anwendung:',
|
||||
appCreateDSLWarning: 'Achtung: Ein unterschiedlicher DSL-Versionsunterschied kann sich auf bestimmte Funktionen auswirken',
|
||||
},
|
||||
editApp: 'App bearbeiten',
|
||||
editAppTitle: 'App-Informationen bearbeiten',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
ImageUploadLegacyTip: 'Sie können jetzt Dateitypvariablen im Startformular erstellen. Wir werden die Funktion zum Hochladen von Bildern in Zukunft nicht mehr unterstützen.',
|
||||
fileUploadTip: 'Die Funktionen zum Hochladen von Bildern wurden auf das Hochladen von Dateien aktualisiert.',
|
||||
featuresDescription: 'Verbessern Sie die Benutzererfahrung von Web-Apps',
|
||||
importWarning: 'Vorsicht',
|
||||
importWarningDetails: 'Der Unterschied zwischen den DSL-Versionen kann sich auf bestimmte Funktionen auswirken',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Umgebungsvariablen',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Art',
|
||||
binaryFileVariable: 'Variable der Binärdatei',
|
||||
extractListPlaceholder: 'Geben Sie den Index des Listeneintrags ein, geben Sie \'/\' ein, fügen Sie die Variable ein',
|
||||
curl: {
|
||||
title: 'Importieren von cURL',
|
||||
placeholder: 'Fügen Sie hier die cURL-Zeichenfolge ein',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Eingabevariablen',
|
||||
|
||||
@ -218,6 +218,10 @@ const translation = {
|
||||
enableText: 'Features Enabled',
|
||||
manage: 'Manage',
|
||||
},
|
||||
documentUpload: {
|
||||
title: 'Document',
|
||||
description: 'Enable Document will allows the model to take in documents and answer questions about them.',
|
||||
},
|
||||
},
|
||||
codegen: {
|
||||
title: 'Code Generator',
|
||||
|
||||
@ -61,10 +61,18 @@ const translation = {
|
||||
hideTemplates: 'Go back to mode selection',
|
||||
Create: 'Create',
|
||||
Cancel: 'Cancel',
|
||||
Confirm: 'Confirm',
|
||||
nameNotEmpty: 'Name cannot be empty',
|
||||
appTemplateNotSelected: 'Please select a template',
|
||||
appTypeRequired: 'Please select an app type',
|
||||
appCreated: 'App created',
|
||||
caution: 'Caution',
|
||||
appCreateDSLWarning: 'Caution: DSL version difference may affect certain features',
|
||||
appCreateDSLErrorTitle: 'Version Incompatibility',
|
||||
appCreateDSLErrorPart1: 'A significant difference in DSL versions has been detected. Forcing the import may cause the application to malfunction.',
|
||||
appCreateDSLErrorPart2: 'Do you want to continue?',
|
||||
appCreateDSLErrorPart3: 'Current application DSL version: ',
|
||||
appCreateDSLErrorPart4: 'System-supported DSL version: ',
|
||||
appCreateFailed: 'Failed to create app',
|
||||
},
|
||||
editApp: 'Edit Info',
|
||||
|
||||
@ -363,7 +363,7 @@ const translation = {
|
||||
deprecated: 'Deprecated',
|
||||
confirmDelete: 'Confirm deletion?',
|
||||
quotaTip: 'Remaining available free tokens',
|
||||
loadPresets: 'Load Presents',
|
||||
loadPresets: 'Load Presets',
|
||||
parameters: 'PARAMETERS',
|
||||
loadBalancing: 'Load balancing',
|
||||
loadBalancingDescription: 'Reduce pressure with multiple sets of credentials.',
|
||||
|
||||
@ -14,7 +14,7 @@ const translation = {
|
||||
prompt: 'Prompt',
|
||||
privatePromptConfigTitle: 'Conversation settings',
|
||||
publicPromptConfigTitle: 'Initial Prompt',
|
||||
configStatusDes: 'Before start, you can modify conversation settings',
|
||||
configStatusDes: 'Before starting, you can modify the conversation settings',
|
||||
configDisabled:
|
||||
'Previous session settings have been used for this session.',
|
||||
startChat: 'Start Chat',
|
||||
|
||||
@ -75,12 +75,14 @@ const translation = {
|
||||
viewDetailInTracingPanel: 'View details',
|
||||
syncingData: 'Syncing data, just a few seconds.',
|
||||
importDSL: 'Import DSL',
|
||||
importDSLTip: 'Current draft will be overwritten. Export workflow as backup before importing.',
|
||||
importDSLTip: 'Current draft will be overwritten.\nExport workflow as backup before importing.',
|
||||
backupCurrentDraft: 'Backup Current Draft',
|
||||
chooseDSL: 'Choose DSL(yml) file',
|
||||
chooseDSL: 'Choose DSL file',
|
||||
overwriteAndImport: 'Overwrite and Import',
|
||||
importFailure: 'Import failure',
|
||||
importSuccess: 'Import success',
|
||||
importFailure: 'Import Failed',
|
||||
importWarning: 'Caution',
|
||||
importWarningDetails: 'DSL version difference may affect certain features',
|
||||
importSuccess: 'Import Successfully',
|
||||
parallelRun: 'Parallel Run',
|
||||
parallelTip: {
|
||||
click: {
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
writeLabel: 'Write Timeout',
|
||||
writePlaceholder: 'Enter write timeout in seconds',
|
||||
},
|
||||
curl: {
|
||||
title: 'Import from cURL',
|
||||
placeholder: 'Paste cURL string here',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Input Variables',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'Cuerpo de la solicitud',
|
||||
pathParams: 'Parámetros de ruta',
|
||||
query: 'Consulta',
|
||||
toc: 'Contenido',
|
||||
},
|
||||
regenerate: 'Regenerar',
|
||||
}
|
||||
|
||||
@ -62,6 +62,14 @@ const translation = {
|
||||
appTypeRequired: 'Por favor, selecciona un tipo de app',
|
||||
appCreated: 'App creada',
|
||||
appCreateFailed: 'Error al crear app',
|
||||
Confirm: 'Confirmar',
|
||||
caution: 'Cautela',
|
||||
appCreateDSLErrorTitle: 'Incompatibilidad de versiones',
|
||||
appCreateDSLErrorPart2: '¿Quieres continuar?',
|
||||
appCreateDSLErrorPart4: 'Versión de DSL compatible con el sistema:',
|
||||
appCreateDSLErrorPart1: 'Se ha detectado una diferencia significativa en las versiones de DSL. Forzar la importación puede hacer que la aplicación no funcione correctamente.',
|
||||
appCreateDSLWarning: 'Precaución: La diferencia de versión de DSL puede afectar a determinadas funciones',
|
||||
appCreateDSLErrorPart3: 'Versión actual de DSL de la aplicación:',
|
||||
},
|
||||
editApp: 'Editar información',
|
||||
editAppTitle: 'Editar información de la app',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
ImageUploadLegacyTip: 'Ahora puede crear variables de tipo de archivo en el formulario de inicio. Ya no admitiremos la función de carga de imágenes en el futuro.',
|
||||
featuresDescription: 'Mejorar la experiencia del usuario de la aplicación web',
|
||||
featuresDocLink: 'Aprende más',
|
||||
importWarning: 'Cautela',
|
||||
importWarningDetails: 'La diferencia de versión de DSL puede afectar a ciertas características',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Variables de Entorno',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Tipo',
|
||||
binaryFileVariable: 'Variable de archivo binario',
|
||||
extractListPlaceholder: 'Introduzca el índice de elementos de la lista, escriba \'/\' insertar variable',
|
||||
curl: {
|
||||
title: 'Importar desde cURL',
|
||||
placeholder: 'Pegar la cadena cURL aquí',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variables de entrada',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'بدنه درخواست',
|
||||
pathParams: 'پارامترهای مسیر',
|
||||
query: 'پرسوجو',
|
||||
toc: 'محتویات',
|
||||
},
|
||||
regenerate: 'بازسازی',
|
||||
}
|
||||
|
||||
@ -66,6 +66,14 @@ const translation = {
|
||||
appTypeRequired: 'لطفاً نوع برنامه را انتخاب کنید',
|
||||
appCreated: 'برنامه ایجاد شد',
|
||||
appCreateFailed: 'ایجاد برنامه ناموفق بود',
|
||||
Confirm: 'تایید',
|
||||
appCreateDSLErrorTitle: 'ناسازگاری نسخه',
|
||||
caution: 'احتیاط',
|
||||
appCreateDSLErrorPart3: 'نسخه DSL برنامه فعلی:',
|
||||
appCreateDSLErrorPart2: 'آیا می خواهید ادامه دهید؟',
|
||||
appCreateDSLErrorPart4: 'نسخه DSL پشتیبانی شده توسط سیستم:',
|
||||
appCreateDSLErrorPart1: 'تفاوت قابل توجهی در نسخه های DSL مشاهده شده است. اجبار به واردات ممکن است باعث اختلال در عملکرد برنامه شود.',
|
||||
appCreateDSLWarning: 'احتیاط: تفاوت نسخه DSL ممکن است بر ویژگی های خاصی تأثیر بگذارد',
|
||||
},
|
||||
editApp: 'ویرایش اطلاعات',
|
||||
editAppTitle: 'ویرایش اطلاعات برنامه',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
featuresDescription: 'بهبود تجربه کاربری برنامه وب',
|
||||
ImageUploadLegacyTip: 'اکنون می توانید متغیرهای نوع فایل را در فرم شروع ایجاد کنید. ما دیگر از ویژگی آپلود تصویر در آینده پشتیبانی نخواهیم کرد.',
|
||||
fileUploadTip: 'ویژگی های آپلود تصویر برای آپلود فایل ارتقا یافته است.',
|
||||
importWarning: 'احتیاط',
|
||||
importWarningDetails: 'تفاوت نسخه DSL ممکن است بر ویژگی های خاصی تأثیر بگذارد',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'متغیرهای محیطی',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
binaryFileVariable: 'متغیر فایل باینری',
|
||||
type: 'نوع',
|
||||
extractListPlaceholder: 'فهرست آیتم لیست را وارد کنید، متغیر درج \'/\' را تایپ کنید',
|
||||
curl: {
|
||||
title: 'وارد کردن از cURL',
|
||||
placeholder: 'رشته cURL را اینجا بچسبانید',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'متغیرهای ورودی',
|
||||
|
||||
@ -76,6 +76,7 @@ const translation = {
|
||||
requestBody: 'Corps de la Requête',
|
||||
pathParams: 'Params de chemin',
|
||||
query: 'Requête',
|
||||
toc: 'Contenu',
|
||||
},
|
||||
loading: 'Chargement',
|
||||
regenerate: 'Régénérer',
|
||||
|
||||
@ -62,6 +62,14 @@ const translation = {
|
||||
appTypeRequired: 'Veuillez sélectionner un type d\'application',
|
||||
appCreated: 'Application créée',
|
||||
appCreateFailed: 'Échec de la création de l\'application',
|
||||
Confirm: 'Confirmer',
|
||||
caution: 'Prudence',
|
||||
appCreateDSLWarning: 'Attention : la différence de version DSL peut affecter certaines fonctionnalités',
|
||||
appCreateDSLErrorPart4: 'Version DSL prise en charge par le système :',
|
||||
appCreateDSLErrorPart1: 'Une différence significative entre les versions DSL a été détectée. Forcer l’importation peut entraîner un dysfonctionnement de l’application.',
|
||||
appCreateDSLErrorTitle: 'Incompatibilité de version',
|
||||
appCreateDSLErrorPart3: 'Version actuelle de l’application DSL :',
|
||||
appCreateDSLErrorPart2: 'Voulez-vous continuer ?',
|
||||
},
|
||||
editApp: 'Modifier les informations',
|
||||
editAppTitle: 'Modifier les informations de l\'application',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
ImageUploadLegacyTip: 'Vous pouvez désormais créer des variables de type de fichier dans le formulaire de démarrage. À l’avenir, nous ne prendrons plus en charge la fonctionnalité de téléchargement d’images.',
|
||||
fileUploadTip: 'Les fonctionnalités de téléchargement d’images ont été mises à niveau vers le téléchargement de fichiers.',
|
||||
featuresDescription: 'Améliorer l’expérience utilisateur de l’application web',
|
||||
importWarning: 'Prudence',
|
||||
importWarningDetails: 'La différence de version DSL peut affecter certaines fonctionnalités',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Variables d\'Environnement',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
binaryFileVariable: 'Variable de fichier binaire',
|
||||
type: 'Type',
|
||||
extractListPlaceholder: 'Entrez l’index de l’élément de liste, tapez \'/\' insérer la variable',
|
||||
curl: {
|
||||
placeholder: 'Collez la chaîne cURL ici',
|
||||
title: 'Importer à partir de cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variables de saisie',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'अनुरोध निकाय',
|
||||
pathParams: 'पथ पैरामीटर',
|
||||
query: 'प्रश्न',
|
||||
toc: 'सामग्री',
|
||||
},
|
||||
regenerate: 'पुनर्जन्म',
|
||||
}
|
||||
|
||||
@ -62,6 +62,14 @@ const translation = {
|
||||
appTypeRequired: 'कृपया एक ऐप प्रकार चुनें',
|
||||
appCreated: 'ऐप बनाया गया',
|
||||
appCreateFailed: 'ऐप बनाने में विफल',
|
||||
Confirm: 'सुदृढ़ करना',
|
||||
appCreateDSLErrorPart4: 'सिस्टम-समर्थित DSL संस्करण:',
|
||||
appCreateDSLErrorPart3: 'वर्तमान अनुप्रयोग डीएसएल संस्करण:',
|
||||
caution: 'सावधानी',
|
||||
appCreateDSLErrorTitle: 'संस्करण असंगति',
|
||||
appCreateDSLErrorPart1: 'डीएसएल संस्करणों में एक महत्वपूर्ण अंतर पाया गया है। आयात को बाध्य करने से अनुप्रयोग में खराबी आ सकती है।',
|
||||
appCreateDSLWarning: 'सावधानी: DSL संस्करण अंतर कुछ सुविधाओं को प्रभावित कर सकता है',
|
||||
appCreateDSLErrorPart2: 'क्या आप जारी रखना चाहते हैं?',
|
||||
},
|
||||
editApp: 'जानकारी संपादित करें',
|
||||
editAppTitle: 'ऐप जानकारी संपादित करें',
|
||||
|
||||
@ -102,6 +102,8 @@ const translation = {
|
||||
featuresDescription: 'वेब ऐप उपयोगकर्ता अनुभव को बेहतर बनाएं',
|
||||
fileUploadTip: 'छवि अपलोड सुविधाओं को फ़ाइल अपलोड में अपग्रेड किया गया है।',
|
||||
ImageUploadLegacyTip: 'अब आप प्रारंभ प्रपत्र में फ़ाइल प्रकार चर बना सकते हैं। हम अब भविष्य में छवि अपलोड सुविधा का समर्थन नहीं करेंगे।',
|
||||
importWarning: 'सावधानी',
|
||||
importWarningDetails: 'डीएसएल संस्करण अंतर कुछ सुविधाओं को प्रभावित कर सकता है',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'पर्यावरण चर',
|
||||
@ -421,6 +423,10 @@ const translation = {
|
||||
type: 'प्रकार',
|
||||
binaryFileVariable: 'बाइनरी फ़ाइल चर',
|
||||
extractListPlaceholder: 'सूची आइटम इंडेक्स दर्ज करें, \'/\' इन्सर्ट वेरिएबल टाइप करें',
|
||||
curl: {
|
||||
placeholder: 'यहां cURL स्ट्रिंग पेस्ट करें',
|
||||
title: 'cURL से आयात करें',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'इनपुट वेरिएबल्स',
|
||||
|
||||
@ -98,6 +98,7 @@ const translation = {
|
||||
requestBody: 'Corpo della Richiesta',
|
||||
pathParams: 'Parametri del Percorso',
|
||||
query: 'Query',
|
||||
toc: 'Contenuto',
|
||||
},
|
||||
regenerate: 'Rigenerare',
|
||||
}
|
||||
|
||||
@ -68,6 +68,14 @@ const translation = {
|
||||
appTypeRequired: 'Seleziona un tipo di app',
|
||||
appCreated: 'App creata',
|
||||
appCreateFailed: 'Creazione dell\'app fallita',
|
||||
Confirm: 'Confermare',
|
||||
appCreateDSLErrorPart2: 'Vuoi continuare?',
|
||||
appCreateDSLErrorPart3: 'Versione DSL dell\'applicazione corrente:',
|
||||
appCreateDSLErrorPart1: 'È stata rilevata una differenza significativa nelle versioni DSL. Forzare l\'importazione può causare il malfunzionamento dell\'applicazione.',
|
||||
caution: 'Cautela',
|
||||
appCreateDSLErrorTitle: 'Incompatibilità di versione',
|
||||
appCreateDSLWarning: 'Attenzione: la differenza di versione DSL può influire su alcune funzionalità',
|
||||
appCreateDSLErrorPart4: 'Versione DSL supportata dal sistema:',
|
||||
},
|
||||
editApp: 'Modifica Info',
|
||||
editAppTitle: 'Modifica Info App',
|
||||
|
||||
@ -103,6 +103,8 @@ const translation = {
|
||||
featuresDescription: 'Migliora l\'esperienza utente dell\'app Web',
|
||||
fileUploadTip: 'Le funzioni di caricamento delle immagini sono state aggiornate al caricamento dei file.',
|
||||
ImageUploadLegacyTip: 'Ora è possibile creare variabili di tipo file nel modulo iniziale. In futuro non supporteremo più la funzione di caricamento delle immagini.',
|
||||
importWarning: 'Cautela',
|
||||
importWarningDetails: 'La differenza di versione DSL può influire su alcune funzionalità',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Variabili d\'Ambiente',
|
||||
@ -425,6 +427,10 @@ const translation = {
|
||||
binaryFileVariable: 'Variabile file binario',
|
||||
type: 'Digitare',
|
||||
extractListPlaceholder: 'Inserisci l\'indice delle voci dell\'elenco, digita \'/\' inserisci la variabile',
|
||||
curl: {
|
||||
placeholder: 'Incolla qui la stringa cURL',
|
||||
title: 'Importazione da cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variabili di Input',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'リクエストボディ',
|
||||
pathParams: 'パスパラメータ',
|
||||
query: 'クエリ',
|
||||
toc: '内容',
|
||||
},
|
||||
regenerate: '再生',
|
||||
}
|
||||
|
||||
@ -359,7 +359,7 @@ const translation = {
|
||||
'content': 'コンテンツ',
|
||||
'required': '必須',
|
||||
'file': {
|
||||
supportFileTypes: 'サッポトされたファイルタイプ',
|
||||
supportFileTypes: 'サポートされたファイルタイプ',
|
||||
image: {
|
||||
name: '画像',
|
||||
},
|
||||
|
||||
@ -67,6 +67,14 @@ const translation = {
|
||||
appTypeRequired: 'アプリの種類を選択してください',
|
||||
appCreated: 'アプリが作成されました',
|
||||
appCreateFailed: 'アプリの作成に失敗しました',
|
||||
Confirm: '確認する',
|
||||
caution: '注意',
|
||||
appCreateDSLErrorPart2: '続行しますか?',
|
||||
appCreateDSLErrorPart4: 'システムがサポートするDSLバージョン:',
|
||||
appCreateDSLErrorPart3: '現在のアプリケーションの DSL バージョン:',
|
||||
appCreateDSLErrorTitle: 'バージョンの非互換性',
|
||||
appCreateDSLWarning: '注意:DSLのバージョンの違いは、特定の機能に影響を与える可能性があります',
|
||||
appCreateDSLErrorPart1: 'DSL バージョンに大きな違いが検出されました。インポートを強制すると、アプリケーションが誤動作する可能性があります。',
|
||||
},
|
||||
editApp: '情報を編集する',
|
||||
editAppTitle: 'アプリ情報を編集する',
|
||||
|
||||
@ -58,7 +58,7 @@ const translation = {
|
||||
registrationNotAllowed: 'アカウントが見つかりません。登録するためにシステム管理者に連絡してください。',
|
||||
},
|
||||
license: {
|
||||
tip: 'Dify Community Editionを開始する前に、GitHubの',
|
||||
tip: 'GitHubのオープンソースライセンスを確認してから、Dify Community Editionを開始してください。',
|
||||
link: 'オープンソースライセンス',
|
||||
},
|
||||
join: '参加する',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
addParallelNode: '並列ノードを追加',
|
||||
parallel: '並列',
|
||||
branch: 'ブランチ',
|
||||
importWarning: '注意',
|
||||
importWarningDetails: 'DSL のバージョンの違いが特定の機能に影響を与える場合があります',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: '環境変数',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'タイプ',
|
||||
binaryFileVariable: 'バイナリファイル変数',
|
||||
extractListPlaceholder: 'リスト項目のインデックスを入力し、変数を挿入 \'/\' と入力します',
|
||||
curl: {
|
||||
title: 'cURLからのインポート',
|
||||
placeholder: 'ここにcURL文字列を貼り付けます',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: '入力変数',
|
||||
|
||||
@ -79,6 +79,7 @@ const translation = {
|
||||
requestBody: '요청 본문',
|
||||
pathParams: '경로 매개변수',
|
||||
query: '쿼리',
|
||||
toc: '목차',
|
||||
},
|
||||
regenerate: '재생성',
|
||||
}
|
||||
|
||||
@ -58,6 +58,14 @@ const translation = {
|
||||
appTypeRequired: '앱 종류를 선택하세요',
|
||||
appCreated: '앱이 생성되었습니다',
|
||||
appCreateFailed: '앱 생성 실패',
|
||||
caution: '주의',
|
||||
Confirm: '확인하다',
|
||||
appCreateDSLErrorPart4: '시스템 지원 DSL 버전:',
|
||||
appCreateDSLErrorTitle: '버전 비호환성',
|
||||
appCreateDSLErrorPart2: '계속하시겠습니까?',
|
||||
appCreateDSLErrorPart3: '현재 응용 프로그램 DSL 버전:',
|
||||
appCreateDSLWarning: '주의: DSL 버전 차이는 특정 기능에 영향을 미칠 수 있습니다.',
|
||||
appCreateDSLErrorPart1: 'DSL 버전에서 상당한 차이가 감지되었습니다. 강제로 가져오면 응용 프로그램이 오작동할 수 있습니다.',
|
||||
},
|
||||
editApp: '정보 편집하기',
|
||||
editAppTitle: '앱 정보 편집하기',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
fileUploadTip: '이미지 업로드 기능이 파일 업로드로 업그레이드되었습니다.',
|
||||
featuresDescription: '웹앱 사용자 경험 향상',
|
||||
ImageUploadLegacyTip: '이제 시작 양식에서 파일 형식 변수를 만들 수 있습니다. 앞으로 이미지 업로드 기능은 더 이상 지원되지 않습니다.',
|
||||
importWarning: '주의',
|
||||
importWarningDetails: 'DSL 버전 차이는 특정 기능에 영향을 미칠 수 있습니다.',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: '환경 변수',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: '형',
|
||||
binaryFileVariable: '바이너리 파일 변수',
|
||||
extractListPlaceholder: '목록 항목 인덱스 입력, \'/\' 변수 삽입',
|
||||
curl: {
|
||||
title: 'cURL에서 가져오기',
|
||||
placeholder: '여기에 cURL 문자열 붙여 넣기',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: '입력 변수',
|
||||
|
||||
@ -96,6 +96,7 @@ const translation = {
|
||||
requestBody: 'Ciało żądania',
|
||||
pathParams: 'Parametry ścieżki',
|
||||
query: 'Zapytanie',
|
||||
toc: 'Treść',
|
||||
},
|
||||
regenerate: 'Ponownie wygenerować',
|
||||
}
|
||||
|
||||
@ -68,6 +68,14 @@ const translation = {
|
||||
appTypeRequired: 'Proszę wybrać typ aplikacji',
|
||||
appCreated: 'Aplikacja utworzona',
|
||||
appCreateFailed: 'Nie udało się utworzyć aplikacji',
|
||||
appCreateDSLErrorPart3: 'Aktualna wersja aplikacji DSL:',
|
||||
appCreateDSLErrorPart2: 'Czy chcesz kontynuować?',
|
||||
Confirm: 'Potwierdzić',
|
||||
caution: 'Ostrożność',
|
||||
appCreateDSLWarning: 'Przestroga: Różnica w wersji DSL może mieć wpływ na niektóre funkcje',
|
||||
appCreateDSLErrorTitle: 'Niezgodność wersji',
|
||||
appCreateDSLErrorPart4: 'Wersja DSL obsługiwana przez system:',
|
||||
appCreateDSLErrorPart1: 'Wykryto istotną różnicę w wersjach DSL. Wymuszenie importu może spowodować nieprawidłowe działanie aplikacji.',
|
||||
},
|
||||
editApp: 'Edytuj informacje',
|
||||
editAppTitle: 'Edytuj informacje o aplikacji',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
fileUploadTip: 'Funkcje przesyłania obrazów zostały zaktualizowane do przesyłania plików.',
|
||||
featuresDescription: 'Ulepszanie środowiska użytkownika aplikacji internetowej',
|
||||
featuresDocLink: 'Dowiedz się więcej',
|
||||
importWarning: 'Ostrożność',
|
||||
importWarningDetails: 'Różnica w wersji DSL może mieć wpływ na niektóre funkcje',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Zmienne Środowiskowe',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Typ',
|
||||
binaryFileVariable: 'Binarna zmienna pliku',
|
||||
extractListPlaceholder: 'Wprowadź indeks elementu listy, wpisz "/" wstaw zmienną',
|
||||
curl: {
|
||||
placeholder: 'Wklej tutaj ciąg cURL',
|
||||
title: 'Importowanie z cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Zmienne wejściowe',
|
||||
|
||||
@ -73,6 +73,7 @@ const translation = {
|
||||
requestBody: 'Corpo da Solicitação',
|
||||
pathParams: 'Parâmetros de Caminho',
|
||||
query: 'Consulta',
|
||||
toc: 'Conteúdo',
|
||||
},
|
||||
play: 'Brincar',
|
||||
loading: 'Carregamento',
|
||||
|
||||
@ -62,6 +62,14 @@ const translation = {
|
||||
appTypeRequired: 'Por favor, selecione um tipo de aplicativo',
|
||||
appCreated: 'Aplicativo criado',
|
||||
appCreateFailed: 'Falha ao criar aplicativo',
|
||||
caution: 'Cuidado',
|
||||
appCreateDSLErrorPart1: 'Uma diferença significativa nas versões DSL foi detectada. Forçar a importação pode causar mau funcionamento do aplicativo.',
|
||||
appCreateDSLErrorPart4: 'Versão DSL suportada pelo sistema:',
|
||||
Confirm: 'Confirmar',
|
||||
appCreateDSLErrorTitle: 'Incompatibilidade de versão',
|
||||
appCreateDSLWarning: 'Cuidado: a diferença de versão DSL pode afetar determinados recursos',
|
||||
appCreateDSLErrorPart3: 'Versão DSL do aplicativo atual:',
|
||||
appCreateDSLErrorPart2: 'Você quer continuar?',
|
||||
},
|
||||
editApp: 'Editar Informações',
|
||||
editAppTitle: 'Editar Informações do Aplicativo',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
featuresDescription: 'Melhore a experiência do usuário do aplicativo Web',
|
||||
ImageUploadLegacyTip: 'Agora você pode criar variáveis de tipo de arquivo no formulário inicial. Não daremos mais suporte ao recurso de upload de imagens no futuro.',
|
||||
fileUploadTip: 'Os recursos de upload de imagens foram atualizados para upload de arquivos.',
|
||||
importWarning: 'Cuidado',
|
||||
importWarningDetails: 'A diferença de versão DSL pode afetar determinados recursos',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Variáveis de Ambiente',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Tipo',
|
||||
binaryFileVariable: 'Variável de arquivo binário',
|
||||
extractListPlaceholder: 'Insira o índice do item da lista, digite \'/\' inserir variável',
|
||||
curl: {
|
||||
placeholder: 'Cole a string cURL aqui',
|
||||
title: 'Importar do cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variáveis de entrada',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'Corpul cererii',
|
||||
pathParams: 'Parametrii căii',
|
||||
query: 'Interogare',
|
||||
toc: 'Conținut',
|
||||
},
|
||||
regenerate: 'Regenera',
|
||||
}
|
||||
|
||||
@ -62,6 +62,14 @@ const translation = {
|
||||
appTypeRequired: 'Vă rugăm să selectați un tip de aplicație',
|
||||
appCreated: 'Aplicația a fost creată',
|
||||
appCreateFailed: 'Crearea aplicației a eșuat',
|
||||
caution: 'Prudență',
|
||||
appCreateDSLErrorPart2: 'Vrei să continui?',
|
||||
Confirm: 'Confirma',
|
||||
appCreateDSLErrorTitle: 'Incompatibilitate versiune',
|
||||
appCreateDSLWarning: 'Atenție: diferența de versiune DSL poate afecta anumite caracteristici',
|
||||
appCreateDSLErrorPart3: 'Versiunea DSL a aplicației curente:',
|
||||
appCreateDSLErrorPart1: 'A fost detectată o diferență semnificativă în versiunile DSL. Forțarea importului poate cauza funcționarea defectuoasă a aplicației.',
|
||||
appCreateDSLErrorPart4: 'Versiune DSL suportată de sistem:',
|
||||
},
|
||||
editApp: 'Editează Info',
|
||||
editAppTitle: 'Editează Info Aplicație',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
featuresDocLink: 'Află mai multe',
|
||||
fileUploadTip: 'Funcțiile de încărcare a imaginilor au fost actualizate la încărcarea fișierelor.',
|
||||
ImageUploadLegacyTip: 'Acum puteți crea variabile de tip de fișier în formularul de pornire. Nu vom mai accepta funcția de încărcare a imaginilor în viitor.',
|
||||
importWarning: 'Prudență',
|
||||
importWarningDetails: 'Diferența de versiune DSL poate afecta anumite caracteristici',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Variabile de Mediu',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Tip',
|
||||
binaryFileVariable: 'Variabilă de fișier binar',
|
||||
extractListPlaceholder: 'Introduceți indexul elementelor din listă, tastați "/" inserați variabila',
|
||||
curl: {
|
||||
placeholder: 'Lipiți șirul cURL aici',
|
||||
title: 'Importați din cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Variabile de intrare',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'Тело запроса',
|
||||
pathParams: 'Параметры пути',
|
||||
query: 'Запрос',
|
||||
toc: 'Содержание',
|
||||
},
|
||||
regenerate: 'Регенерировать',
|
||||
}
|
||||
|
||||
@ -66,6 +66,14 @@ const translation = {
|
||||
appTypeRequired: 'Пожалуйста, выберите тип приложения',
|
||||
appCreated: 'Приложение создано',
|
||||
appCreateFailed: 'Не удалось создать приложение',
|
||||
caution: 'Осторожность',
|
||||
appCreateDSLErrorPart2: 'Хотите продолжить?',
|
||||
Confirm: 'Подтверждать',
|
||||
appCreateDSLErrorTitle: 'Несовместимость версий',
|
||||
appCreateDSLErrorPart3: 'Актуальная версия приложения DSL:',
|
||||
appCreateDSLErrorPart4: 'Поддерживаемая системой версия DSL:',
|
||||
appCreateDSLWarning: 'Внимание: разница в версиях DSL может повлиять на некоторые функции',
|
||||
appCreateDSLErrorPart1: 'Обнаружена существенная разница в версиях DSL. Принудительный импорт может привести к сбою в работе приложения.',
|
||||
},
|
||||
editApp: 'Редактировать информацию',
|
||||
editAppTitle: 'Редактировать информацию о приложении',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
fileUploadTip: 'Функции загрузки изображений были обновлены до загрузки файлов.',
|
||||
featuresDescription: 'Улучшение взаимодействия с пользователем веб-приложения',
|
||||
ImageUploadLegacyTip: 'Теперь вы можете создавать переменные типа файла в стартовой форме. В будущем мы больше не будем поддерживать функцию загрузки изображений.',
|
||||
importWarning: 'Осторожность',
|
||||
importWarningDetails: 'Разница в версии DSL может повлиять на некоторые функции',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Переменные среды',
|
||||
@ -408,6 +410,10 @@ const translation = {
|
||||
type: 'Тип',
|
||||
binaryFileVariable: 'Переменная двоичного файла',
|
||||
extractListPlaceholder: 'Введите индекс элемента списка, введите \'/\' вставьте переменную',
|
||||
curl: {
|
||||
placeholder: 'Вставьте сюда строку cURL',
|
||||
title: 'Импорт из cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Входные переменные',
|
||||
|
||||
@ -78,6 +78,7 @@ const translation = {
|
||||
requestBody: 'Telo zahteve',
|
||||
pathParams: 'Parametri poti',
|
||||
query: 'Poizvedba',
|
||||
toc: 'Vsebino',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -66,6 +66,14 @@ const translation = {
|
||||
appTypeRequired: 'Izberite vrsto aplikacije',
|
||||
appCreated: 'Aplikacija ustvarjena',
|
||||
appCreateFailed: 'Ustvarjanje aplikacije ni uspelo',
|
||||
appCreateDSLErrorTitle: 'Nezdružljivost različice',
|
||||
caution: 'Previdnost',
|
||||
Confirm: 'Potrditi',
|
||||
appCreateDSLErrorPart1: 'Odkrita je bila pomembna razlika v različicah DSL. Vsiljevanje uvoza lahko povzroči nepravilno delovanje aplikacije.',
|
||||
appCreateDSLErrorPart3: 'Trenutna različica aplikacije DSL:',
|
||||
appCreateDSLErrorPart4: 'Sistemsko podprta različica DSL:',
|
||||
appCreateDSLWarning: 'Pozor: Razlika v različici DSL lahko vpliva na nekatere funkcije',
|
||||
appCreateDSLErrorPart2: 'Želite nadaljevati?',
|
||||
},
|
||||
editApp: 'Uredi informacije',
|
||||
editAppTitle: 'Uredi informacije o aplikaciji',
|
||||
|
||||
@ -99,6 +99,8 @@ const translation = {
|
||||
featuresDocLink: 'Izvedi več',
|
||||
featuresDescription: 'Izboljšajte uporabniško izkušnjo spletne aplikacije',
|
||||
ImageUploadLegacyTip: 'Zdaj lahko ustvarite spremenljivke vrste datoteke v začetnem obrazcu. V prihodnje ne bomo več podpirali funkcije nalaganja slik.',
|
||||
importWarning: 'Previdnost',
|
||||
importWarningDetails: 'Razlika v različici DSL lahko vpliva na nekatere funkcije',
|
||||
},
|
||||
env: {
|
||||
envPanelTitle: 'Spremenljivke okolja',
|
||||
@ -845,6 +847,10 @@ const translation = {
|
||||
apiPlaceholder: 'Vnesite URL, vnesite \'/\' vstavi spremenljivko',
|
||||
extractListPlaceholder: 'Vnesite indeks elementa seznama, vnesite \'/\' vstavi spremenljivko',
|
||||
params: 'Params',
|
||||
curl: {
|
||||
title: 'Uvoz iz cURL',
|
||||
placeholder: 'Tukaj prilepite niz cURL',
|
||||
},
|
||||
},
|
||||
code: {
|
||||
inputVars: 'Vhodne spremenljivke',
|
||||
|
||||
@ -77,6 +77,7 @@ const translation = {
|
||||
requestBody: 'Request Body',
|
||||
pathParams: 'Path Params',
|
||||
query: 'Query',
|
||||
toc: 'Içeriği',
|
||||
},
|
||||
regenerate: 'Yenilemek',
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user