Merge branch 'main' into feat/rag-pipeline

pull/21398/head
twwu 11 months ago
commit db154e33b7

@ -369,6 +369,7 @@ class DatasetTagsApi(DatasetApiResource):
)
parser.add_argument("tag_id", nullable=False, required=True, help="Id of a tag.", type=str)
args = parser.parse_args()
args["type"] = "knowledge"
tag = TagService.update_tags(args, args.get("tag_id"))
binding_count = TagService.get_tag_binding_count(args.get("tag_id"))

@ -206,12 +206,16 @@ class DocumentAddByFileApi(DatasetApiResource):
knowledge_config = KnowledgeConfig(**args)
DocumentService.document_create_args_validate(knowledge_config)
dataset_process_rule = dataset.latest_process_rule if "process_rule" not in args else None
if not knowledge_config.original_document_id and not dataset_process_rule and not knowledge_config.process_rule:
raise ValueError("process_rule is required.")
try:
documents, batch = DocumentService.save_document_with_dataset_id(
dataset=dataset,
knowledge_config=knowledge_config,
account=dataset.created_by_account,
dataset_process_rule=dataset.latest_process_rule if "process_rule" not in args else None,
dataset_process_rule=dataset_process_rule,
created_from="api",
)
except ProviderTokenNotInitError as ex:

@ -720,7 +720,7 @@ STOPWORDS = {
"",
"",
"",
" ",
" ",
"0",
"1",
"2",
@ -731,16 +731,6 @@ STOPWORDS = {
"7",
"8",
"9",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",
"",

@ -261,7 +261,7 @@ class OracleVector(BaseVector):
words = pseg.cut(query)
current_entity = ""
for word, pos in words:
if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: # nr: 人名, ns: 地名, nt: 机构名
if pos in {"nr", "Ng", "eng", "nz", "n", "ORG", "v"}: # nr: 人名ns: 地名,nt: 机构名
current_entity += word
else:
if current_entity:

@ -86,8 +86,8 @@ class KnowledgeRetrievalNode(LLMNode):
return NodeRunResult(
status=WorkflowNodeExecutionStatus.FAILED, inputs=variables, error="Query is required."
)
# TODO(-LAN-): Move this check outside.
# check rate limit
if self.tenant_id:
knowledge_rate_limit = FeatureService.get_knowledge_rate_limit(self.tenant_id)
if knowledge_rate_limit.enabled:
current_time = int(time.time() * 1000)

@ -46,6 +46,8 @@ class TagService:
@staticmethod
def get_tag_by_tag_name(tag_type: str, current_tenant_id: str, tag_name: str) -> list:
if not tag_type or not tag_name:
return []
tags = (
db.session.query(Tag)
.filter(Tag.name == tag_name, Tag.tenant_id == current_tenant_id, Tag.type == tag_type)
@ -88,7 +90,7 @@ class TagService:
@staticmethod
def update_tags(args: dict, tag_id: str) -> Tag:
if TagService.get_tag_by_tag_name(args["type"], current_user.current_tenant_id, args["name"]):
if TagService.get_tag_by_tag_name(args.get("type", ""), current_user.current_tenant_id, args.get("name", "")):
raise ValueError("Tag name already exists")
tag = db.session.query(Tag).filter(Tag.id == tag_id).first()
if not tag:

@ -231,7 +231,7 @@ export const useFile = (fileConfig: FileUpload) => {
url: res.url,
}
if (!isAllowedFileExtension(res.name, res.mime_type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) {
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') })
notify({ type: 'error', message: `${t('common.fileUploader.fileExtensionNotSupport')} ${file.type}` })
handleRemoveFile(uploadingFile.id)
}
if (!checkSizeLimit(newFile.supportFileType, newFile.size))
@ -257,7 +257,7 @@ export const useFile = (fileConfig: FileUpload) => {
const handleLocalFileUpload = useCallback((file: File) => {
if (!isAllowedFileExtension(file.name, file.type, fileConfig.allowed_file_types || [], fileConfig.allowed_file_extensions || [])) {
notify({ type: 'error', message: t('common.fileUploader.fileExtensionNotSupport') })
notify({ type: 'error', message: `${t('common.fileUploader.fileExtensionNotSupport')} ${file.type}` })
return
}
const allowedFileTypes = fileConfig.allowed_file_types

@ -22,7 +22,7 @@ import { FILE_EXTS } from '../prompt-editor/constants'
jest.mock('mime', () => ({
__esModule: true,
default: {
getExtension: jest.fn(),
getAllExtensions: jest.fn(),
},
}))
@ -58,12 +58,27 @@ describe('file-uploader utils', () => {
describe('getFileExtension', () => {
it('should get extension from mimetype', () => {
jest.mocked(mime.getExtension).mockReturnValue('pdf')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
expect(getFileExtension('file', 'application/pdf')).toBe('pdf')
})
it('should get extension from mimetype and file name 1', () => {
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
expect(getFileExtension('file.pdf', 'application/pdf')).toBe('pdf')
})
it('should get extension from mimetype with multiple ext candidates with filename hint', () => {
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
expect(getFileExtension('file.pem', 'application/x-x509-ca-cert')).toBe('pem')
})
it('should get extension from mimetype with multiple ext candidates without filename hint', () => {
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['der', 'crt', 'pem']))
expect(getFileExtension('file', 'application/x-x509-ca-cert')).toBe('der')
})
it('should get extension from filename if mimetype fails', () => {
jest.mocked(mime.getExtension).mockReturnValue(null)
jest.mocked(mime.getAllExtensions).mockReturnValue(null)
expect(getFileExtension('file.txt', '')).toBe('txt')
expect(getFileExtension('file.txt.docx', '')).toBe('docx')
expect(getFileExtension('file', '')).toBe('')
@ -76,157 +91,157 @@ describe('file-uploader utils', () => {
describe('getFileAppearanceType', () => {
it('should identify gif files', () => {
jest.mocked(mime.getExtension).mockReturnValue('gif')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['gif']))
expect(getFileAppearanceType('image.gif', 'image/gif'))
.toBe(FileAppearanceTypeEnum.gif)
})
it('should identify image files', () => {
jest.mocked(mime.getExtension).mockReturnValue('jpg')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpg']))
expect(getFileAppearanceType('image.jpg', 'image/jpeg'))
.toBe(FileAppearanceTypeEnum.image)
jest.mocked(mime.getExtension).mockReturnValue('jpeg')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['jpeg']))
expect(getFileAppearanceType('image.jpeg', 'image/jpeg'))
.toBe(FileAppearanceTypeEnum.image)
jest.mocked(mime.getExtension).mockReturnValue('png')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['png']))
expect(getFileAppearanceType('image.png', 'image/png'))
.toBe(FileAppearanceTypeEnum.image)
jest.mocked(mime.getExtension).mockReturnValue('webp')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webp']))
expect(getFileAppearanceType('image.webp', 'image/webp'))
.toBe(FileAppearanceTypeEnum.image)
jest.mocked(mime.getExtension).mockReturnValue('svg')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['svg']))
expect(getFileAppearanceType('image.svg', 'image/svgxml'))
.toBe(FileAppearanceTypeEnum.image)
})
it('should identify video files', () => {
jest.mocked(mime.getExtension).mockReturnValue('mp4')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp4']))
expect(getFileAppearanceType('video.mp4', 'video/mp4'))
.toBe(FileAppearanceTypeEnum.video)
jest.mocked(mime.getExtension).mockReturnValue('mov')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mov']))
expect(getFileAppearanceType('video.mov', 'video/quicktime'))
.toBe(FileAppearanceTypeEnum.video)
jest.mocked(mime.getExtension).mockReturnValue('mpeg')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpeg']))
expect(getFileAppearanceType('video.mpeg', 'video/mpeg'))
.toBe(FileAppearanceTypeEnum.video)
jest.mocked(mime.getExtension).mockReturnValue('webm')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['webm']))
expect(getFileAppearanceType('video.web', 'video/webm'))
.toBe(FileAppearanceTypeEnum.video)
})
it('should identify audio files', () => {
jest.mocked(mime.getExtension).mockReturnValue('mp3')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mp3']))
expect(getFileAppearanceType('audio.mp3', 'audio/mpeg'))
.toBe(FileAppearanceTypeEnum.audio)
jest.mocked(mime.getExtension).mockReturnValue('m4a')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['m4a']))
expect(getFileAppearanceType('audio.m4a', 'audio/mp4'))
.toBe(FileAppearanceTypeEnum.audio)
jest.mocked(mime.getExtension).mockReturnValue('wav')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['wav']))
expect(getFileAppearanceType('audio.wav', 'audio/vnd.wav'))
.toBe(FileAppearanceTypeEnum.audio)
jest.mocked(mime.getExtension).mockReturnValue('amr')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['amr']))
expect(getFileAppearanceType('audio.amr', 'audio/AMR'))
.toBe(FileAppearanceTypeEnum.audio)
jest.mocked(mime.getExtension).mockReturnValue('mpga')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mpga']))
expect(getFileAppearanceType('audio.mpga', 'audio/mpeg'))
.toBe(FileAppearanceTypeEnum.audio)
})
it('should identify code files', () => {
jest.mocked(mime.getExtension).mockReturnValue('html')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['html']))
expect(getFileAppearanceType('index.html', 'text/html'))
.toBe(FileAppearanceTypeEnum.code)
})
it('should identify PDF files', () => {
jest.mocked(mime.getExtension).mockReturnValue('pdf')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
expect(getFileAppearanceType('doc.pdf', 'application/pdf'))
.toBe(FileAppearanceTypeEnum.pdf)
})
it('should identify markdown files', () => {
jest.mocked(mime.getExtension).mockReturnValue('md')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['md']))
expect(getFileAppearanceType('file.md', 'text/markdown'))
.toBe(FileAppearanceTypeEnum.markdown)
jest.mocked(mime.getExtension).mockReturnValue('markdown')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['markdown']))
expect(getFileAppearanceType('file.markdown', 'text/markdown'))
.toBe(FileAppearanceTypeEnum.markdown)
jest.mocked(mime.getExtension).mockReturnValue('mdx')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['mdx']))
expect(getFileAppearanceType('file.mdx', 'text/mdx'))
.toBe(FileAppearanceTypeEnum.markdown)
})
it('should identify excel files', () => {
jest.mocked(mime.getExtension).mockReturnValue('xlsx')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xlsx']))
expect(getFileAppearanceType('doc.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'))
.toBe(FileAppearanceTypeEnum.excel)
jest.mocked(mime.getExtension).mockReturnValue('xls')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xls']))
expect(getFileAppearanceType('doc.xls', 'application/vnd.ms-excel'))
.toBe(FileAppearanceTypeEnum.excel)
})
it('should identify word files', () => {
jest.mocked(mime.getExtension).mockReturnValue('doc')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['doc']))
expect(getFileAppearanceType('doc.doc', 'application/msword'))
.toBe(FileAppearanceTypeEnum.word)
jest.mocked(mime.getExtension).mockReturnValue('docx')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['docx']))
expect(getFileAppearanceType('doc.docx', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'))
.toBe(FileAppearanceTypeEnum.word)
})
it('should identify word files', () => {
jest.mocked(mime.getExtension).mockReturnValue('ppt')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['ppt']))
expect(getFileAppearanceType('doc.ppt', 'application/vnd.ms-powerpoint'))
.toBe(FileAppearanceTypeEnum.ppt)
jest.mocked(mime.getExtension).mockReturnValue('pptx')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pptx']))
expect(getFileAppearanceType('doc.pptx', 'application/vnd.openxmlformats-officedocument.presentationml.presentation'))
.toBe(FileAppearanceTypeEnum.ppt)
})
it('should identify document files', () => {
jest.mocked(mime.getExtension).mockReturnValue('txt')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['txt']))
expect(getFileAppearanceType('file.txt', 'text/plain'))
.toBe(FileAppearanceTypeEnum.document)
jest.mocked(mime.getExtension).mockReturnValue('csv')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['csv']))
expect(getFileAppearanceType('file.csv', 'text/csv'))
.toBe(FileAppearanceTypeEnum.document)
jest.mocked(mime.getExtension).mockReturnValue('msg')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['msg']))
expect(getFileAppearanceType('file.msg', 'application/vnd.ms-outlook'))
.toBe(FileAppearanceTypeEnum.document)
jest.mocked(mime.getExtension).mockReturnValue('eml')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['eml']))
expect(getFileAppearanceType('file.eml', 'message/rfc822'))
.toBe(FileAppearanceTypeEnum.document)
jest.mocked(mime.getExtension).mockReturnValue('xml')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['xml']))
expect(getFileAppearanceType('file.xml', 'application/rssxml'))
.toBe(FileAppearanceTypeEnum.document)
jest.mocked(mime.getExtension).mockReturnValue('epub')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['epub']))
expect(getFileAppearanceType('file.epub', 'application/epubzip'))
.toBe(FileAppearanceTypeEnum.document)
})
it('should handle null mime extension', () => {
jest.mocked(mime.getExtension).mockReturnValue(null)
jest.mocked(mime.getAllExtensions).mockReturnValue(null)
expect(getFileAppearanceType('file.txt', 'text/plain'))
.toBe(FileAppearanceTypeEnum.document)
})
@ -360,7 +375,7 @@ describe('file-uploader utils', () => {
describe('isAllowedFileExtension', () => {
it('should validate allowed file extensions', () => {
jest.mocked(mime.getExtension).mockReturnValue('pdf')
jest.mocked(mime.getAllExtensions).mockReturnValue(new Set(['pdf']))
expect(isAllowedFileExtension(
'test.pdf',
'application/pdf',

@ -42,19 +42,38 @@ export const fileUpload: FileUpload = ({
})
}
const additionalExtensionMap = new Map<string, string[]>([
['text/x-markdown', ['md']],
])
export const getFileExtension = (fileName: string, fileMimetype: string, isRemote?: boolean) => {
let extension = ''
if (fileMimetype)
extension = mime.getExtension(fileMimetype) || ''
if (fileName && !extension) {
let extensions = new Set<string>()
if (fileMimetype) {
const extensionsFromMimeType = mime.getAllExtensions(fileMimetype) || new Set<string>()
const additionalExtensions = additionalExtensionMap.get(fileMimetype) || []
extensions = new Set<string>([
...extensionsFromMimeType,
...additionalExtensions,
])
}
let extensionInFileName = ''
if (fileName) {
const fileNamePair = fileName.split('.')
const fileNamePairLength = fileNamePair.length
if (fileNamePairLength > 1)
extension = fileNamePair[fileNamePairLength - 1]
if (fileNamePairLength > 1) {
extensionInFileName = fileNamePair[fileNamePairLength - 1].toLowerCase()
if (extensions.has(extensionInFileName))
extension = extensionInFileName
}
}
if (!extension) {
if (extensions.size > 0)
extension = extensions.values().next().value.toLowerCase()
else
extension = ''
extension = extensionInFileName
}
if (isRemote)

@ -33,5 +33,6 @@ export const preprocessThinkTag = (content: string) => {
return flow([
(str: string) => str.replace(thinkOpenTagRegex, '<details data-think=true>\n'),
(str: string) => str.replace(thinkCloseTagRegex, '\n[ENDTHINKFLAG]</details>'),
(str: string) => str.replace(/(<\/details>)(?![^\S\r\n]*[\r\n])(?![^\S\r\n]*$)/g, '$1\n'),
])(content)
}

@ -192,15 +192,15 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
- original_document_id が渡されない場合、新しい操作が実行され、process_rule が必要です。
- <code>indexing_technique</code> インデックスモード
- <code>high_quality</code> 高品質: 埋め込みモデルを使用してベクトルデータベースインデックスを構築
- <code>economy</code> 経済: キーワードテーブルインデックスの反転インデックスを構築
- <code>high_quality</code> 高品質埋め込みモデルを使用してベクトルデータベースインデックスを構築
- <code>economy</code> 経済キーワードテーブルインデックスの反転インデックスを構築
- <code>doc_form</code> インデックス化された内容の形式
- <code>text_model</code> テキストドキュメントは直接埋め込まれます; `economy` モードではこの形式がデフォルト
- <code>hierarchical_model</code> 親子モード
- <code>qa_model</code> Q&A モード: 分割されたドキュメントの質問と回答ペアを生成し、質問を埋め込みます
- <code>qa_model</code> Q&A モード分割されたドキュメントの質問と回答ペアを生成し、質問を埋め込みます
- <code>doc_language</code> Q&A モードでは、ドキュメントの言語を指定します。例: <code>English</code>, <code>Chinese</code>
- <code>doc_language</code> Q&A モードでは、ドキュメントの言語を指定します。例<code>English</code>, <code>Chinese</code>
- <code>process_rule</code> 処理ルール
- <code>mode</code> (string) クリーニング、セグメンテーションモード、自動 / カスタム
@ -214,7 +214,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
- <code>segmentation</code> (object) セグメンテーションルール
- <code>separator</code> カスタムセグメント識別子。現在は 1 つの区切り文字のみ設定可能。デフォルトは \n
- <code>max_tokens</code> 最大長 (トークン) デフォルトは 1000
- <code>parent_mode</code> 親チャンクの検索モード: <code>full-doc</code> 全文検索 / <code>paragraph</code> 段落検索
- <code>parent_mode</code> 親チャンクの検索モード<code>full-doc</code> 全文検索 / <code>paragraph</code> 段落検索
- <code>subchunk_segmentation</code> (object) 子チャンクルール
- <code>separator</code> セグメンテーション識別子。現在は 1 つの区切り文字のみ許可。デフォルトは <code>***</code>
- <code>max_tokens</code> 最大長 (トークン) は親チャンクの長さより短いことを検証する必要があります
@ -324,7 +324,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi
- <code>partial_members</code> 一部のメンバー
</Property>
<Property name='provider' type='string' key='provider'>
プロバイダー (オプション、デフォルト: vendor)
プロバイダー (オプション、デフォルトvendor)
- <code>vendor</code> ベンダー
- <code>external</code> 外部ナレッジ
</Property>

@ -60,7 +60,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
<Property name='files' type='array[object]' key='files'>
上传的文件。
- `type` (string) 支持类型:图片 `image`(目前仅支持图片格式) 。
- `transfer_method` (string) 传递方式:
- `transfer_method` (string) 传递方式
- `remote_url`: 图片地址。
- `local_file`: 上传文件。
- `url` 图片地址。(仅当传递方式为 `remote_url` 时)。
@ -622,10 +622,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
用于获取应用的 WebApp 设置
### Response
- `title` (string) WebApp 名称
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
- `chat_color_theme` (string) 聊天颜色主题hex 格式
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
- `icon_type` (string) 图标类型`emoji`-表情,`image`-图片
- `icon` (string) 图标,如果是 `emoji` 类型,则是 emoji 表情符号,如果是 `image` 类型,则是图片 URL
- `icon_background` (string) hex 格式的背景色
- `icon_url` (string) 图标 URL
- `description` (string) 描述
@ -879,7 +879,7 @@ ___
动作,只能是 'enable' 或 'disable'
</Property>
<Property name='embedding_provider_name' type='string' key='embedding_provider_name'>
指定的嵌入模型提供商, 必须先在系统内设定好接入的模型对应的是provider字段
指定的嵌入模型提供商必须先在系统内设定好接入的模型,对应的是 provider 字段
</Property>
<Property name='embedding_model_name' type='string' key='embedding_model_name'>
指定的嵌入模型,对应的是 model 字段

@ -1347,10 +1347,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
用于获取应用的 WebApp 设置
### Response
- `title` (string) WebApp 名称
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
- `chat_color_theme` (string) 聊天颜色主题hex 格式
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
- `icon_type` (string) 图标类型`emoji`-表情,`image`-图片
- `icon` (string) 图标,如果是 `emoji` 类型,则是 emoji 表情符号,如果是 `image` 类型,则是图片 URL
- `icon_background` (string) hex 格式的背景色
- `icon_url` (string) 图标 URL
- `description` (string) 描述
@ -1604,7 +1604,7 @@ ___
动作,只能是 'enable' 或 'disable'
</Property>
<Property name='embedding_provider_name' type='string' key='embedding_provider_name'>
指定的嵌入模型提供商, 必须先在系统内设定好接入的模型对应的是provider字段
指定的嵌入模型提供商必须先在系统内设定好接入的模型,对应的是 provider 字段
</Property>
<Property name='embedding_model_name' type='string' key='embedding_model_name'>
指定的嵌入模型,对应的是 model 字段

@ -1353,10 +1353,10 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx'
用于获取应用的 WebApp 设置
### Response
- `title` (string) WebApp 名称
- `chat_color_theme` (string) 聊天颜色主题, hex 格式
- `chat_color_theme` (string) 聊天颜色主题hex 格式
- `chat_color_theme_inverted` (bool) 聊天颜色主题是否反转
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
- `icon_type` (string) 图标类型`emoji`-表情,`image`-图片
- `icon` (string) 图标,如果是 `emoji` 类型,则是 emoji 表情符号,如果是 `image` 类型,则是图片 URL
- `icon_background` (string) hex 格式的背景色
- `icon_url` (string) 图标 URL
- `description` (string) 描述

@ -534,7 +534,7 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
- `workflow_run` (object) Workflow 执行日志
- `id` (string) 标识
- `version` (string) 版本
- `status` (string) 执行状态, `running` / `succeeded` / `failed` / `stopped`
- `status` (string) 执行状态`running` / `succeeded` / `failed` / `stopped`
- `error` (string) (可选) 错误
- `elapsed_time` (float) 耗时,单位秒
- `total_tokens` (int) 消耗的 token 数量
@ -741,8 +741,8 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等
用于获取应用的 WebApp 设置
### Response
- `title` (string) WebApp 名称
- `icon_type` (string) 图标类型, `emoji`-表情, `image`-图片
- `icon` (string) 图标, 如果是 `emoji` 类型, 则是 emoji 表情符号, 如果是 `image` 类型, 则是图片 URL
- `icon_type` (string) 图标类型`emoji`-表情,`image`-图片
- `icon` (string) 图标,如果是 `emoji` 类型,则是 emoji 表情符号,如果是 `image` 类型,则是图片 URL
- `icon_background` (string) hex 格式的背景色
- `icon_url` (string) 图标 URL
- `description` (string) 描述

@ -24,7 +24,7 @@ const translation = {
desc: {
front: 'Your information and use of Education Verified status are subject to our',
and: 'and',
end: '. By submitting',
end: '. By submitting:',
termsOfService: 'Terms of Service',
privacyPolicy: 'Privacy Policy',
},

@ -1,6 +1,6 @@
const translation = {
title: 'ज्ञान सेटिंग्ज',
desc: 'यहां आप ज्ञान की संपत्ति और कार्य प्रक्रियाओं को modify कर सकते हैं',
desc: 'यहां आप ज्ञान की संपत्ति और कार्य प्रक्रियाओं को modify कर सकते हैं.',
form: {
name: 'ज्ञान नाम',
namePlaceholder: 'कृपया ज्ञान नाम दर्ज करें',

@ -44,7 +44,7 @@ const translation = {
title: '一括インポート',
csvUploadTitle: 'CSV ファイルをここにドラッグ&ドロップするか、',
browse: '参照',
tip: 'CSVファイルは以下の構造に準拠する必要があります:',
tip: 'CSV ファイルは以下の構造に準拠する必要があります',
question: '質問',
answer: '回答',
contentTitle: 'チャンクの内容',

@ -228,7 +228,7 @@ const translation = {
description: 'コードジェネレーターは、設定されたモデルを使用して指示に基づいて高品質なコードを生成します。明確で詳細な指示を提供してください。',
instruction: '指示',
instructionPlaceholder: '生成したいコードの詳細な説明を入力してください。',
noDataLine1: '左側に使用例を記入してください,',
noDataLine1: '左側に使用例を記入してください',
noDataLine2: 'コードのプレビューがこちらに表示されます。',
generate: '生成',
generatedCodeTitle: '生成されたコード',
@ -247,7 +247,7 @@ const translation = {
instructionPlaceHolder: '具体的で明確な指示を入力してください。',
generate: '生成',
resTitle: '生成されたプロンプト',
noDataLine1: '左側に使用例を記入してください,',
noDataLine1: '左側に使用例を記入してください',
noDataLine2: 'オーケストレーションのプレビューがこちらに表示されます。',
apply: '適用',
noData: '左側にユースケースを入力すると、こちらでプレビューができます。',
@ -298,7 +298,7 @@ const translation = {
message: '変更が破棄され、最後に公開された構成が復元されます。',
},
errorMessage: {
nameOfKeyRequired: 'キーの名前: {{key}} が必要です',
nameOfKeyRequired: 'キーの名前{{key}} が必要です',
valueOfVarRequired: '{{key}} の値は空にできません',
queryRequired: 'リクエストテキストが必要です。',
waitForResponse: '前のメッセージへの応答が完了するまでお待ちください。',
@ -376,7 +376,7 @@ const translation = {
custom: {
name: '他のファイルタイプ',
description: '他のファイルタイプを指定する。',
createPlaceholder: '+ 拡張子, 例:.doc',
createPlaceholder: '+ 拡張子例:.doc',
},
},
'uploadFileTypes': 'アップロードされたファイルのタイプ',

@ -73,9 +73,9 @@ const translation = {
appCreateFailed: 'アプリの作成に失敗しました',
Confirm: '確認する',
caution: '注意',
appCreateDSLErrorPart2: '続行しますか?',
appCreateDSLErrorPart4: 'システムがサポートするDSLバージョン:',
appCreateDSLErrorPart3: '現在のアプリケーションの DSL バージョン:',
appCreateDSLErrorPart2: '続行しますか',
appCreateDSLErrorPart4: 'システムがサポートする DSL バージョン:',
appCreateDSLErrorPart3: '現在のアプリケーションの DSL バージョン',
appCreateDSLErrorTitle: 'バージョンの非互換性',
appCreateDSLWarning: '注意:DSL のバージョンの違いは、特定の機能に影響を与える可能性があります',
appCreateDSLErrorPart1: 'DSL バージョンに大きな違いが検出されました。インポートを強制すると、アプリケーションが誤動作する可能性があります。',
@ -95,7 +95,7 @@ const translation = {
forAdvanced: '上級ユーザー向け',
chooseAppType: 'アプリタイプを選択',
learnMore: '詳細情報',
noIdeaTip: 'アイデアがありませんか?テンプレートをご覧ください',
noIdeaTip: 'アイデアがありませんかテンプレートをご覧ください',
chatbotShortDescription: '簡単なセットアップの LLM ベースのチャットボット',
chatbotUserDescription: '簡単な設定で LLM ベースのチャットボットを迅速に構築します。Chatflow は後で切り替えることができます。',
workflowUserDescription: 'ドラッグ&ドロップの簡易性で自律型 AI ワークフローを視覚的に構築',

@ -124,7 +124,7 @@ const translation = {
description: 'オープンソース版の無料プラン',
price: '無料',
btnText: 'コミュニティ版を始めましょう',
includesTitle: '無料機能:',
includesTitle: '無料機能',
features: [
'パブリックリポジトリの全コア機能',
'シングルワークスペース',
@ -138,7 +138,7 @@ const translation = {
price: '従量制',
priceTip: 'クラウドマーケットプレイス基準',
btnText: 'プレミアム版を取得',
includesTitle: 'コミュニティ版機能に加えて:',
includesTitle: 'コミュニティ版機能に加えて',
comingSoon: 'Microsoft Azure & Google Cloud 近日対応',
features: [
'クラウドプロバイダーによる自己管理',
@ -154,7 +154,7 @@ const translation = {
price: 'カスタム',
priceTip: '年間契約専用',
btnText: '営業に相談',
includesTitle: 'プレミアム版機能に加えて:',
includesTitle: 'プレミアム版機能に加えて',
features: [
'エンタープライズ向け拡張ソリューション',
'商用ライセンス認可',

@ -565,10 +565,10 @@ const translation = {
citation: {
title: '引用',
linkToDataset: 'ナレッジへのリンク',
characters: '文字数:',
hitCount: '検索回数:',
vectorHash: 'ベクトルハッシュ:',
hitScore: '検索スコア:',
characters: '文字数',
hitCount: '検索回数',
vectorHash: 'ベクトルハッシュ',
hitScore: '検索スコア',
},
inputPlaceholder: '{{botName}} と話す',
thought: '思考',

@ -85,9 +85,9 @@ const translation = {
excludePaths: 'パスを除外する',
includeOnlyPaths: 'パスのみを含める',
extractOnlyMainContent: 'メインコンテンツのみを抽出する (ヘッダー、ナビ、フッターなどは抽出しない)',
exceptionErrorTitle: 'Firecrawl ジョブの実行中に例外が発生しました:',
exceptionErrorTitle: 'Firecrawl ジョブの実行中に例外が発生しました',
unknownError: '不明なエラー',
totalPageScraped: 'スクレイピングされた総ページ数:',
totalPageScraped: 'スクレイピングされた総ページ数',
selectAll: 'すべて選択',
resetAll: 'すべてリセット',
scrapTimeInfo: '{{time}} 秒以内に合計 {{total}} ページをスクレイピングしました',
@ -175,7 +175,7 @@ const translation = {
separatorTip: '区切り文字は、テキストを区切るために使用される文字です。\\n\\n と \\n は、段落と行を区切るために一般的に使用される区切り記号です。カンマ (\\n\\n,\\n) と組み合わせると、最大チャンク長を超えると、段落は行で区切られます。自分で定義した特別な区切り文字を使用することもできます (例:***)。',
maxLengthCheck: 'チャンクの最大長は {{limit}} 未満にする必要があります',
previewChunkTip: 'プレビューを読み込むには、左側の \'チャンクをプレビュー\' ボタンをクリックしてください',
previewChunkCount: '推定チャンク数: {{count}}',
previewChunkCount: '推定チャンク数{{count}}',
switch: '切り替え',
qaSwitchHighQualityTipTitle: 'Q&A 形式には高品質なインデックスが必要です',
qaSwitchHighQualityTipContent: '現在、高品質なインデックス作成のみが Q&A 形式の分割をサポートしています。高品質モードに切り替えますか?',

@ -65,7 +65,7 @@ const translation = {
title: '一括追加',
csvUploadTitle: 'CSV ファイルをここにドラッグアンドドロップするか、',
browse: '参照',
tip: 'CSVファイルは次の構造に準拠する必要があります:',
tip: 'CSV ファイルは次の構造に準拠する必要があります',
question: '質問',
answer: '回答',
contentTitle: 'チャンクの内容',
@ -359,7 +359,7 @@ const translation = {
characters_one: '文字',
characters_other: '文字',
hitCount: '検索回数',
vectorHash: 'ベクトルハッシュ: ',
vectorHash: 'ベクトルハッシュ',
questionPlaceholder: 'ここに質問を追加',
questionEmpty: '質問は空にできません',
answerPlaceholder: 'ここに回答を追加',

@ -76,7 +76,7 @@ const translation = {
verify: '確かめる',
verificationCodePlaceholder: '6 桁のコードを入力してください',
useAnotherMethod: '別の方法を使用する',
didNotReceiveCode: 'コードが届きませんか?',
didNotReceiveCode: 'コードが届きませんか',
resend: '再送',
verificationCode: '認証コード',
tips: '<strong>確認コードを{{email}}に送信します。</strong>',

@ -162,7 +162,7 @@ const translation = {
},
error: {
fetchReleasesError: 'リリースを取得できません。後でもう一度お試しください。',
inValidGitHubUrl: '無効なGitHub URLです。有効なURLを次の形式で入力してください: https://github.com/owner/repo',
inValidGitHubUrl: '無効な GitHub URL です。有効な URL を次の形式で入力してください:https://github.com/owner/repo',
noReleasesFound: 'リリースは見つかりません。GitHub リポジトリまたは入力 URL を確認してください。',
},
marketplace: {

@ -56,7 +56,7 @@ const translation = {
noData: 'AI がコンテンツを生成します',
csvUploadTitle: 'CSV ファイルをドロップするか',
browse: 'ファイルを選択',
csvStructureTitle: 'CSV形式要件:',
csvStructureTitle: 'CSV 形式要件:',
downloadTemplate: 'テンプレートを取得',
field: '',
batchFailed: {
@ -67,9 +67,9 @@ const translation = {
errorMsg: {
empty: 'ファイル内容が空です',
fileStructNotMatch: 'ファイル形式が不正です',
emptyLine: '{{rowIndex}}行目: 内容が空です',
invalidLine: '{{rowIndex}}行目: {{varName}}の入力が必要です',
moreThanMaxLengthLine: '{{rowIndex}}行目: {{varName}}が制限長({{maxLength}})を超過',
emptyLine: '{{rowIndex}}行目内容が空です',
invalidLine: '{{rowIndex}}行目{{varName}}の入力が必要です',
moreThanMaxLengthLine: '{{rowIndex}}行目{{varName}}が制限長({{maxLength}})を超過',
atLeastOne: '1 行以上のデータが必要です',
},
},

@ -40,7 +40,7 @@ const translation = {
name: '名前',
toolNamePlaceHolder: 'ツール名を入力してください',
nameForToolCall: 'ツールコールの名前',
nameForToolCallPlaceHolder: '機械認識に使用される名前, 例えば、getCurrentWeather、list_pets',
nameForToolCallPlaceHolder: '機械認識に使用される名前例えば、getCurrentWeather、list_pets',
nameForToolCallTip: '数字、文字、アンダースコアのみがサポートされます。',
description: 'ツールの説明',
descriptionPlaceholder: 'ツールの使い方の簡単な説明。例えば、特定の場所の温度を知るためなど。',

@ -336,7 +336,7 @@ const translation = {
defaultValue: {
title: 'デフォルト値',
desc: '例外発生時のデフォルト出力',
tip: '例外発生時に返される値:',
tip: '例外発生時に返される値',
inLog: 'ノード例外 - デフォルト値を出力',
output: 'デフォルト値出力',
},
@ -363,7 +363,7 @@ const translation = {
retryFailedTimes: '{{times}}回再試行失敗',
times: '回',
ms: 'ミリ秒',
retries: '再試行回数: {{num}}',
retries: '再試行回数{{num}}',
},
},
start: {
@ -623,7 +623,7 @@ const translation = {
assigner: {
'assignedVariable': '代入された変数',
'writeMode': '書き込みモード',
'writeModeTip': '代入された変数が配列の場合, 末尾に追記モードを追加する。',
'writeModeTip': '代入された変数が配列の場合末尾に追記モードを追加する。',
'over-write': '上書き',
'append': '追記',
'plus': 'プラス',
@ -732,7 +732,7 @@ const translation = {
parallelModeEnableDesc: '並列モードでは、イテレーション内のタスクは並列実行をサポートします。これは、右側のプロパティパネルで構成できます。',
parallelModeEnableTitle: 'パラレルモード有効',
MaxParallelismDesc: '最大並列処理は、1 回の反復で同時に実行されるタスクの数を制御するために使用されます。',
answerNodeWarningDesc: '並列モードの警告: 応答ノード、会話変数の割り当て、およびイテレーション内の永続的な読み取り/書き込み操作により、例外が発生する可能性があります。',
answerNodeWarningDesc: '並列モードの警告応答ノード、会話変数の割り当て、およびイテレーション内の永続的な読み取り/書き込み操作により、例外が発生する可能性があります。',
},
loop: {
deleteTitle: 'ループノードを削除しますか?',
@ -760,8 +760,8 @@ const translation = {
inputMode: '入力モード',
exitConditionTip: 'ループノードには少なくとも 1 つの終了条件が必要です',
loopNode: 'ループノード',
currentLoopCount: '現在のループ回数: {{count}}',
totalLoopCount: '総ループ回数: {{count}}',
currentLoopCount: '現在のループ回数{{count}}',
totalLoopCount: '総ループ回数{{count}}',
error_other: '{{count}} エラー',
error_one: '{{count}} エラー',
comma: ',',
@ -791,7 +791,7 @@ const translation = {
},
inputVar: '入力変数',
learnMore: '詳細はこちら',
supportFileTypes: 'サポートするファイルタイプ: {{types}}。',
supportFileTypes: 'サポートするファイルタイプ{{types}}。',
},
listFilter: {
outputVars: {

@ -228,7 +228,7 @@ const translation = {
'logic': 'Logic',
'transform': 'Chuyển đổi',
'utilities': 'Tiện ích',
'noResult': 'Không tìm thấy kết quả phù hợp',
'noResult': 'Không tìm thấy kế. t quả phù hợp',
'searchTool': 'Công cụ tìm kiếm',
'agent': 'Chiến lược đại lý',
'plugin': 'Plugin',

@ -1,6 +1,6 @@
const translation = {
welcome: {
firstStepTip: '开始之前,',
firstStepTip: '开始之前',
enterKeyTip: '请先在下方输入你的 OpenAI API Key',
getKeyTip: '从 OpenAI 获取你的 API Key',
placeholder: '你的 OpenAI API Key例如 sk-xxxx',

@ -23,7 +23,7 @@ const translation = {
importFromDSLFile: '文件',
importFromDSLUrl: 'URL',
importFromDSLUrlPlaceholder: '输入 DSL 文件的 URL',
deleteAppConfirmTitle: '确认删除应用?',
deleteAppConfirmTitle: '确认删除应用',
deleteAppConfirmContent:
'删除应用将无法撤销。用户将不能访问你的应用,所有 Prompt 编排配置和日志均将一并被删除。',
appDeleted: '应用已删除',
@ -170,7 +170,7 @@ const translation = {
publicKey: '公钥',
secretKey: '密钥',
viewDocsLink: '查看 {{key}} 的文档',
removeConfirmTitle: '删除 {{key}} 配置?',
removeConfirmTitle: '删除 {{key}} 配置',
removeConfirmContent: '当前配置正在使用中,删除它将关闭追踪功能。',
},
weave: {

@ -156,7 +156,7 @@ const translation = {
exploreMarketplace: '探索 Marketplace',
pluginsTips: '集成第三方插件或创建与 ChatGPT 兼容的 AI 插件。',
datasets: '知识库',
datasetsTips: '即将到来: 上传自己的长文本数据,或通过 Webhook 集成自己的数据源',
datasetsTips: '即将到来上传自己的长文本数据,或通过 Webhook 集成自己的数据源',
newApp: '创建应用',
newDataset: '创建知识库',
tools: '工具',
@ -418,7 +418,7 @@ const translation = {
getFreeTokens: '获得免费 Tokens',
priorityUsing: '优先使用',
deprecated: '已弃用',
confirmDelete: '确认删除?',
confirmDelete: '确认删除',
quotaTip: '剩余免费额度',
loadPresets: '加载预设',
parameters: '参数',

@ -93,9 +93,9 @@ const translation = {
excludePaths: '排除路径',
includeOnlyPaths: '仅包含路径',
extractOnlyMainContent: '仅提取主要内容(无标题、导航、页脚等)',
exceptionErrorTitle: '运行时发生异常:',
exceptionErrorTitle: '运行时发生异常',
unknownError: '未知错误',
totalPageScraped: '抓取页面总数:',
totalPageScraped: '抓取页面总数',
selectAll: '全选',
resetAll: '重置全部',
scrapTimeInfo: '总共在 {{time}}秒 内抓取了 {{total}} 个页面',

@ -78,8 +78,8 @@ const translation = {
createDatasetIntro: '导入您自己的文本数据或通过 Webhook 实时写入数据以增强 LLM 的上下文。',
deleteDatasetConfirmTitle: '要删除知识库吗?',
deleteDatasetConfirmContent:
'删除知识库是不可逆的。用户将无法再访问您的知识库,所有的提示配置和日志将被永久删除。',
datasetUsedByApp: '某些应用正在使用该知识库。应用将无法再使用该知识库,所有的提示配置和日志将被永久删除。',
'删除知识库是不可逆的。用户将无法再访问您的知识库所有的提示配置和日志将被永久删除。',
datasetUsedByApp: '某些应用正在使用该知识库。应用将无法再使用该知识库所有的提示配置和日志将被永久删除。',
datasetDeleted: '知识库已删除',
datasetDeleteFailed: '删除知识库失败',
selectExternalKnowledgeAPI: {
@ -216,7 +216,7 @@ const translation = {
builtIn: '内置',
builtInDescription: '内置元数据是系统预定义的元数据,您可以在此处查看和管理内置元数据。',
deleteTitle: '确定删除',
deleteContent: '你确定要删除元数据 "{{name}}" 吗?',
deleteContent: '你确定要删除元数据 "{{name}}" 吗',
},
documentMetadata: {
metadataToolTip: '元数据是关于文档的数据,用于描述文档的属性。元数据可以帮助您更好地组织和管理文档。',

@ -64,7 +64,7 @@ const translation = {
registrationNotAllowed: '账户不存在,请联系系统管理员注册账户',
},
license: {
tip: '启动 Dify 社区版之前, 请阅读 GitHub 上的',
tip: '启动 Dify 社区版之前请阅读 GitHub 上的',
link: '开源协议',
},
join: '加入 ',

@ -126,7 +126,7 @@ const translation = {
pluginInfo: '插件信息',
delete: '移除插件',
deleteContentLeft: '是否要移除 ',
deleteContentRight: ' 插件?',
deleteContentRight: ' 插件',
usedInApps: '此插件正在 {{num}} 个应用中使用。',
},
installModal: {

@ -68,8 +68,8 @@ const translation = {
empty: '上传文件的内容不能为空',
fileStructNotMatch: '上传文件的内容与结构不匹配',
emptyLine: '第 {{rowIndex}} 行的内容为空',
invalidLine: '第 {{rowIndex}} 行: {{varName}}值必填',
moreThanMaxLengthLine: '第 {{rowIndex}} 行: {{varName}}值超过最大长度 {{maxLength}}',
invalidLine: '第 {{rowIndex}} 行{{varName}}值必填',
moreThanMaxLengthLine: '第 {{rowIndex}} 行{{varName}}值超过最大长度 {{maxLength}}',
atLeastOne: '上传文件的内容不能少于一条',
},
},

@ -135,7 +135,7 @@ const translation = {
infoAndSetting: '信息和设置',
},
noCustomTool: {
title: '没有自定义工具!',
title: '没有自定义工具',
content: '在此统一添加和管理你的自定义工具,方便构建应用时使用。',
createTool: '创建工具',
},

@ -796,7 +796,7 @@ const translation = {
outputVars: {
text: '提取的文本',
},
supportFileTypes: '支持的文件类型: {{types}}。',
supportFileTypes: '支持的文件类型{{types}}。',
learnMore: '了解更多',
},
listFilter: {

@ -1,6 +1,6 @@
const translation = {
welcome: {
firstStepTip: '開始之前,',
firstStepTip: '開始之前',
enterKeyTip: '請先在下方輸入你的 OpenAI API Key',
getKeyTip: '從 OpenAI 獲取你的 API Key',
placeholder: '你的 OpenAI API Key例如 sk-xxxx',

@ -15,7 +15,7 @@ const translation = {
exportFailed: '匯出 DSL 失敗',
importDSL: '匯入 DSL 檔案',
createFromConfigFile: '透過 DSL 檔案建立',
deleteAppConfirmTitle: '確認刪除應用?',
deleteAppConfirmTitle: '確認刪除應用',
deleteAppConfirmContent:
'刪除應用將無法復原。使用者將無法存取你的應用,所有 Prompt 設定和日誌都將一併被刪除。',
appDeleted: '應用已刪除',

@ -142,7 +142,7 @@ const translation = {
plugins: '外掛',
pluginsTips: '整合第三方外掛或建立與 ChatGPT 相容的 AI 外掛。',
datasets: '知識庫',
datasetsTips: '即將到來: 上傳自己的長文字資料,或透過 Webhook 整合自己的資料來源',
datasetsTips: '即將到來上傳自己的長文字資料,或透過 Webhook 整合自己的資料來源',
newApp: '建立應用',
newDataset: '建立知識庫',
tools: '工具',
@ -398,7 +398,7 @@ const translation = {
getFreeTokens: '獲得免費 Tokens',
priorityUsing: '優先使用',
deprecated: '已棄用',
confirmDelete: '確認刪除?',
confirmDelete: '確認刪除',
quotaTip: '剩餘免費額度',
loadPresets: '載入預設',
parameters: '引數',

@ -7,7 +7,7 @@ const translation = {
createDatasetIntro: '匯入您自己的文字資料或透過 Webhook 實時寫入資料以增強 LLM 的上下文。',
deleteDatasetConfirmTitle: '要刪除知識庫嗎?',
deleteDatasetConfirmContent:
'刪除知識庫是不可逆的。使用者將無法再訪問您的知識庫,所有的提示配置和日誌將被永久刪除。',
'刪除知識庫是不可逆的。使用者將無法再訪問您的知識庫所有的提示配置和日誌將被永久刪除。',
datasetUsedByApp: '這些知識正被一些應用程序使用。應用程序將無法再使用這些知識,所有提示配置和日誌將被永久刪除。',
datasetDeleted: '知識庫已刪除',
datasetDeleteFailed: '刪除知識庫失敗',

@ -57,7 +57,7 @@ const translation = {
registrationNotAllowed: '找不到帳戶。請聯繫系統管理員進行註冊。',
},
license: {
tip: '啟動 Dify 社群版之前, 請閱讀 GitHub 上的',
tip: '啟動 Dify 社群版之前請閱讀 GitHub 上的',
link: '開源協議',
},
join: '加入',

@ -66,8 +66,8 @@ const translation = {
empty: '上傳檔案的內容不能為空',
fileStructNotMatch: '上傳檔案的內容與結構不匹配',
emptyLine: '第 {{rowIndex}} 行的內容為空',
invalidLine: '第 {{rowIndex}} 行: {{varName}}值必填',
moreThanMaxLengthLine: '第 {{rowIndex}} 行: {{varName}}值超過最大長度 {{maxLength}}',
invalidLine: '第 {{rowIndex}} 行{{varName}}值必填',
moreThanMaxLengthLine: '第 {{rowIndex}} 行{{varName}}值超過最大長度 {{maxLength}}',
atLeastOne: '上傳檔案的內容不能少於一條',
},
execution: '執行',

@ -123,7 +123,7 @@ const translation = {
file: '檔',
},
noCustomTool: {
title: '沒有自定義工具!',
title: '沒有自定義工具',
content: '在此統一新增和管理你的自定義工具,方便構建應用時使用。',
createTool: '建立工具',
},

Loading…
Cancel
Save