Commit 91e56dda authored by takatost's avatar takatost

Merge branch 'feat/workflow-backend' into deploy/dev

# Conflicts:
#	api/commands.py
parents 7d786468 13c5d829
# Dify Open Source License
# Open Source License
The Dify project is licensed under the Apache License 2.0, with the following additional conditions:
Dify is licensed under the Apache License 2.0, with the following additional conditions:
1. Dify is permitted to be used for commercialization, such as using Dify as a "backend-as-a-service" for your other applications, or delivering it to enterprises as an application development platform. However, when the following conditions are met, you must contact the producer to obtain a commercial license:
1. Dify may be utilized commercially, including as a backend service for other applications or as an application development platform for enterprises. Should the conditions below be met, a commercial license must be obtained from the producer:
a. Multi-tenant SaaS service: Unless explicitly authorized by Dify in writing, you may not use the Dify.AI source code to operate a multi-tenant SaaS service that is similar to the Dify.AI service edition.
b. LOGO and copyright information: In the process of using Dify, you may not remove or modify the LOGO or copyright information in the Dify console.
a. Multi-tenant SaaS service: Unless explicitly authorized by Dify in writing, you may not use the Dify source code to operate a multi-tenant environment.
- Tenant Definition: Within the context of Dify, one tenant corresponds to one workspace. The workspace provides a separated area for each tenant's data and configurations.
b. LOGO and copyright information: In the process of using Dify's frontend components, you may not remove or modify the LOGO or copyright information in the Dify console or applications. This restriction is inapplicable to uses of Dify that do not involve its frontend components.
Please contact business@dify.ai by email to inquire about licensing matters.
2. As a contributor, you should agree that your contributed code:
2. As a contributor, you should agree that:
a. The producer can adjust the open-source agreement to be more strict or relaxed.
b. Can be used for commercial purposes, such as Dify's cloud business.
a. The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
b. Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
Apart from this, all other rights and restrictions follow the Apache License 2.0. If you need more detailed information, you can refer to the full version of Apache License 2.0.
Apart from the specific conditions mentioned above, all other rights and restrictions follow the Apache License 2.0. Detailed information about the Apache License 2.0 can be found at http://www.apache.org/licenses/LICENSE-2.0.
The interactive design of this product is protected by appearance patent.
© 2023 LangGenius, Inc.
© 2024 LangGenius, Inc.
----------
......
......@@ -15,7 +15,7 @@ from libs.rsa import generate_key_pair
from models.account import Tenant
from models.dataset import Dataset, DatasetCollectionBinding, DocumentSegment
from models.dataset import Document as DatasetDocument
from models.model import Account, App, AppMode, Conversation
from models.model import Account, App, AppMode, AppModelConfig, AppAnnotationSetting, Conversation, MessageAnnotation
from models.provider import Provider, ProviderModel
......@@ -125,7 +125,114 @@ def reset_encrypt_key_pair():
@click.command('vdb-migrate', help='migrate vector db.')
def vdb_migrate():
@click.option('--scope', default='all', prompt=False, help='The scope of vector database to migrate, Default is All.')
def vdb_migrate(scope: str):
if scope in ['knowledge', 'all']:
migrate_knowledge_vector_database()
if scope in ['annotation', 'all']:
migrate_annotation_vector_database()
def migrate_annotation_vector_database():
"""
Migrate annotation datas to target vector database .
"""
click.echo(click.style('Start migrate annotation data.', fg='green'))
create_count = 0
skipped_count = 0
total_count = 0
page = 1
while True:
try:
# get apps info
apps = db.session.query(App).filter(
App.status == 'normal'
).order_by(App.created_at.desc()).paginate(page=page, per_page=50)
except NotFound:
break
page += 1
for app in apps:
total_count = total_count + 1
click.echo(f'Processing the {total_count} app {app.id}. '
+ f'{create_count} created, {skipped_count} skipped.')
try:
click.echo('Create app annotation index: {}'.format(app.id))
app_annotation_setting = db.session.query(AppAnnotationSetting).filter(
AppAnnotationSetting.app_id == app.id
).first()
if not app_annotation_setting:
skipped_count = skipped_count + 1
click.echo('App annotation setting is disabled: {}'.format(app.id))
continue
# get dataset_collection_binding info
dataset_collection_binding = db.session.query(DatasetCollectionBinding).filter(
DatasetCollectionBinding.id == app_annotation_setting.collection_binding_id
).first()
if not dataset_collection_binding:
click.echo('App annotation collection binding is not exist: {}'.format(app.id))
continue
annotations = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app.id).all()
dataset = Dataset(
id=app.id,
tenant_id=app.tenant_id,
indexing_technique='high_quality',
embedding_model_provider=dataset_collection_binding.provider_name,
embedding_model=dataset_collection_binding.model_name,
collection_binding_id=dataset_collection_binding.id
)
documents = []
if annotations:
for annotation in annotations:
document = Document(
page_content=annotation.question,
metadata={
"annotation_id": annotation.id,
"app_id": app.id,
"doc_id": annotation.id
}
)
documents.append(document)
vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id'])
click.echo(f"Start to migrate annotation, app_id: {app.id}.")
try:
vector.delete()
click.echo(
click.style(f'Successfully delete vector index for app: {app.id}.',
fg='green'))
except Exception as e:
click.echo(
click.style(f'Failed to delete vector index for app {app.id}.',
fg='red'))
raise e
if documents:
try:
click.echo(click.style(
f'Start to created vector index with {len(documents)} annotations for app {app.id}.',
fg='green'))
vector.create(documents)
click.echo(
click.style(f'Successfully created vector index for app {app.id}.', fg='green'))
except Exception as e:
click.echo(click.style(f'Failed to created vector index for app {app.id}.', fg='red'))
raise e
click.echo(f'Successfully migrated app annotation {app.id}.')
create_count += 1
except Exception as e:
click.echo(
click.style('Create app annotation index error: {} {}'.format(e.__class__.__name__, str(e)),
fg='red'))
continue
click.echo(
click.style(f'Congratulations! Create {create_count} app annotation indexes, and skipped {skipped_count} apps.',
fg='green'))
def migrate_knowledge_vector_database():
"""
Migrate vector database datas to target vector database .
"""
......
......@@ -259,6 +259,7 @@ class ToolApiProviderPreviousTestApi(Resource):
parser = reqparse.RequestParser()
parser.add_argument('tool_name', type=str, required=True, nullable=False, location='json')
parser.add_argument('provider_name', type=str, required=False, nullable=False, location='json')
parser.add_argument('credentials', type=dict, required=True, nullable=False, location='json')
parser.add_argument('parameters', type=dict, required=True, nullable=False, location='json')
parser.add_argument('schema_type', type=str, required=True, nullable=False, location='json')
......@@ -268,6 +269,7 @@ class ToolApiProviderPreviousTestApi(Resource):
return ToolManageService.test_api_tool_preview(
current_user.current_tenant_id,
args['provider_name'] if args['provider_name'] else '',
args['tool_name'],
args['credentials'],
args['parameters'],
......
......@@ -140,6 +140,7 @@ class MilvusVector(BaseVector):
connections.connect(alias=alias, uri=uri, user=self._client_config.user, password=self._client_config.password)
from pymilvus import utility
if utility.has_collection(self._collection_name, using=alias):
utility.drop_collection(self._collection_name, None, using=alias)
def text_exists(self, id: str) -> bool:
......
......@@ -231,6 +231,9 @@ class QdrantVector(BaseVector):
def delete(self):
from qdrant_client.http import models
from qdrant_client.http.exceptions import UnexpectedResponse
try:
filter = models.Filter(
must=[
models.FieldCondition(
......@@ -245,7 +248,13 @@ class QdrantVector(BaseVector):
filter=filter
),
)
except UnexpectedResponse as e:
# Collection does not exist, so return
if e.status_code == 404:
return
# Some other error occurred, so re-raise the exception
else:
raise e
def delete_by_ids(self, ids: list[str]) -> None:
from qdrant_client.http import models
......
- google
- bing
- duckduckgo
- yahoo
- wikipedia
- arxiv
- pubmed
- dalle
- azuredalle
- stablediffusion
- webscraper
- youtube
- wolframalpha
- maths
- github
- chart
- time
- yahoo
- stablediffusion
- vectorizer
- youtube
- gaode
- maths
- wecom
......@@ -16,7 +16,8 @@ class BingProvider(BuiltinToolProviderController):
user_id='',
tool_parameters={
"query": "test",
"result_type": "link"
"result_type": "link",
"enable_webpages": True,
},
)
except Exception as e:
......
<svg height="512" viewBox="0 0 448 512" width="448" xmlns="http://www.w3.org/2000/svg"><path d="m48 32c-26.5 0-48 21.5-48 48v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-352c0-26.5-21.5-48-48-48zm69.56445 64s49.09165 11.12539 46.59571 94.78125c0 0 41.47034-117.171493 204.5664 1.64844 0 42.78788-.31445 172.24246-.31445 223.57031-176.89733-149.87989-207.38477-22.06836-207.38477-22.06836 0-79.8558-81.753902-70.33984-81.753902-70.33984v-212.65039s18.755175 1.4021 38.291012 11.11132zm86.14649 98.2832-24.00196 141.34961h36.5625l11.81446-81.3789h.37304l32.44727 81.3789h14.63281l33.93946-81.3789h.37304l10.31446 81.3789h36.7832l-21.40234-141.34961h-36.5625l-30.38868 75.54102-28.69531-75.54102z"/></svg>
\ No newline at end of file
from core.tools.errors import ToolProviderCredentialValidationError
from core.tools.provider.builtin.pubmed.tools.pubmed_search import PubMedSearchTool
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
class PubMedProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
try:
PubMedSearchTool().fork_tool_runtime(
meta={
"credentials": credentials,
}
).invoke(
user_id='',
tool_parameters={
"query": "John Doe",
},
)
except Exception as e:
raise ToolProviderCredentialValidationError(str(e))
\ No newline at end of file
identity:
author: Pink Banana
name: pubmed
label:
en_US: PubMed
zh_Hans: PubMed
description:
en_US: A search engine for biomedical literature.
zh_Hans: 一款生物医学文献搜索引擎。
icon: icon.svg
from typing import Any
from langchain.tools import PubmedQueryRun
from pydantic import BaseModel, Field
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool
class PubMedInput(BaseModel):
query: str = Field(..., description="Search query.")
class PubMedSearchTool(BuiltinTool):
"""
Tool for performing a search using PubMed search engine.
"""
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]:
"""
Invoke the PubMed search tool.
Args:
user_id (str): The ID of the user invoking the tool.
tool_parameters (dict[str, Any]): The parameters for the tool invocation.
Returns:
ToolInvokeMessage | list[ToolInvokeMessage]: The result of the tool invocation.
"""
query = tool_parameters.get('query', '')
if not query:
return self.create_text_message('Please input query')
tool = PubmedQueryRun(args_schema=PubMedInput)
result = tool.run(query)
return self.create_text_message(self.summary(user_id=user_id, content=result))
\ No newline at end of file
identity:
name: pubmed_search
author: Pink Banana
label:
en_US: PubMed Search
zh_Hans: PubMed 搜索
description:
human:
en_US: PubMed® comprises more than 35 million citations for biomedical literature from MEDLINE, life science journals, and online books. Citations may include links to full text content from PubMed Central and publisher web sites.
zh_Hans: PubMed® 包含来自 MEDLINE、生命科学期刊和在线书籍的超过 3500 万篇生物医学文献引用。引用可能包括来自 PubMed Central 和出版商网站的全文内容链接。
llm: Perform searches on PubMed and get results.
parameters:
- name: query
type: string
required: true
label:
en_US: Query string
zh_Hans: 查询语句
human_description:
en_US: The search query.
zh_Hans: 搜索查询语句。
llm_description: Key words for searching
form: llm
......@@ -70,7 +70,7 @@ class StableDiffusionTool(BuiltinTool):
if not base_url:
return self.create_text_message('Please input base_url')
if 'model' in tool_parameters:
if 'model' in tool_parameters and tool_parameters['model']:
self.runtime.credentials['model'] = tool_parameters['model']
model = self.runtime.credentials.get('model', None)
......
from typing import Any, Union
import httpx
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool
class WecomRepositoriesTool(BuiltinTool):
def _invoke(self, user_id: str, tool_parameters: dict[str, Any]
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
invoke tools
"""
content = tool_parameters.get('content', '')
if not content:
return self.create_text_message('Invalid parameter content')
hook_key = tool_parameters.get('hook_key', '')
if not hook_key:
return self.create_text_message('Invalid parameter hook_key')
msgtype = 'text'
api_url = 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send'
headers = {
'Content-Type': 'application/json',
}
params = {
'key': hook_key,
}
payload = {
"msgtype": msgtype,
"text": {
"content": content,
}
}
try:
res = httpx.post(api_url, headers=headers, params=params, json=payload)
if res.is_success:
return self.create_text_message("Text message sent successfully")
else:
return self.create_text_message(
f"Failed to send the text message, status code: {res.status_code}, response: {res.text}")
except Exception as e:
return self.create_text_message("Failed to send message to group chat bot. {}".format(e))
identity:
name: wecom_group_bot
author: Bowen Liang
label:
en_US: Send Group Message
zh_Hans: 发送群消息
pt_BR: Send Group Message
icon: icon.svg
description:
human:
en_US: Sending a group message on Wecom via the webhook of group bot
zh_Hans: 通过企业微信的群机器人webhook发送群消息
pt_BR: Sending a group message on Wecom via the webhook of group bot
llm: A tool for sending messages to a chat group on Wecom(企业微信) .
parameters:
- name: hook_key
type: string
required: true
label:
en_US: Wecom Group bot webhook key
zh_Hans: 群机器人webhook的key
pt_BR: Wecom Group bot webhook key
human_description:
en_US: Wecom Group bot webhook key
zh_Hans: 群机器人webhook的key
pt_BR: Wecom Group bot webhook key
form: form
- name: content
type: string
required: true
label:
en_US: content
zh_Hans: 消息内容
pt_BR: content
human_description:
en_US: Content to sent to the group.
zh_Hans: 群消息文本
pt_BR: Content to sent to the group.
llm_description: Content of the message
form: llm
from core.tools.provider.builtin.wecom.tools.wecom_group_bot import WecomRepositoriesTool
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController
class GaodeProvider(BuiltinToolProviderController):
def _validate_credentials(self, credentials: dict) -> None:
WecomRepositoriesTool()
pass
identity:
author: Bowen Liang
name: wecom
label:
en_US: Wecom
zh_Hans: 企业微信
pt_BR: Wecom
description:
en_US: Wecom group bot
zh_Hans: 企业微信群机器人
pt_BR: Wecom group bot
icon: icon.png
credentials_for_provider:
import json
from json import dumps
from typing import Any, Union
from urllib.parse import urlencode
import httpx
import requests
......@@ -203,6 +204,8 @@ class ApiTool(Tool):
if 'Content-Type' in headers:
if headers['Content-Type'] == 'application/json':
body = dumps(body)
elif headers['Content-Type'] == 'application/x-www-form-urlencoded':
body = urlencode(body)
else:
body = body
......
......@@ -52,7 +52,7 @@ safetensors==0.3.2
zhipuai==1.0.7
werkzeug~=3.0.1
pymilvus==2.3.0
qdrant-client==1.6.4
qdrant-client==1.7.3
cohere~=4.44
pyyaml~=6.0.1
numpy~=1.25.2
......
......@@ -498,12 +498,16 @@ class ToolManageService:
@staticmethod
def test_api_tool_preview(
tenant_id: str, tool_name: str, credentials: dict, parameters: dict, schema_type: str, schema: str
tenant_id: str,
provider_name: str,
tool_name: str,
credentials: dict,
parameters: dict,
schema_type: str,
schema: str
):
"""
test api tool before adding api tool provider
1. parse schema into tool bundle
"""
if schema_type not in [member.value for member in ApiProviderSchemaType]:
raise ValueError(f'invalid schema type {schema_type}')
......@@ -518,6 +522,12 @@ class ToolManageService:
if tool_bundle is None:
raise ValueError(f'invalid tool name {tool_name}')
db_provider: ApiToolProvider = db.session.query(ApiToolProvider).filter(
ApiToolProvider.tenant_id == tenant_id,
ApiToolProvider.name == provider_name,
).first()
if not db_provider:
# create a fake db provider
db_provider = ApiToolProvider(
tenant_id='', user_id='', name='', icon='',
......@@ -539,6 +549,19 @@ class ToolManageService:
# load tools into provider entity
provider_controller.load_bundled_tools(tool_bundles)
# decrypt credentials
if db_provider.id:
tool_configuration = ToolConfiguration(
tenant_id=tenant_id,
provider_controller=provider_controller
)
decrypted_credentials = tool_configuration.decrypt_tool_credentials(credentials)
# check if the credential has changed, save the original credential
masked_credentials = tool_configuration.mask_tool_credentials(decrypted_credentials)
for name, value in credentials.items():
if name in masked_credentials and value == masked_credentials[name]:
credentials[name] = decrypted_credentials[name]
try:
provider_controller.validate_credentials_format(credentials)
# get tool
......
......@@ -2,14 +2,11 @@
import { useTranslation } from 'react-i18next'
import classNames from 'classnames'
import Link from 'next/link'
import { useContext } from 'use-context-selector'
import s from './index.module.css'
import Modal from '@/app/components/base/modal'
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
import type { LangGeniusVersionResponse } from '@/models/common'
import { IS_CE_EDITION } from '@/config'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n/language'
import LogoSite from '@/app/components/base/logo/logo-site'
type IAccountSettingProps = {
......@@ -25,7 +22,6 @@ export default function AccountAbout({
onCancel,
}: IAccountSettingProps) {
const { t } = useTranslation()
const { locale } = useContext(I18n)
const isLatest = langeniusVersionInfo.current_version === langeniusVersionInfo.latest_version
return (
......@@ -48,8 +44,8 @@ export default function AccountAbout({
IS_CE_EDITION
? <Link href={'https://github.com/langgenius/dify/blob/main/LICENSE'} target='_blank' rel='noopener noreferrer'>Open Source License</Link>
: <>
<Link href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/user-agreement/privacy-policy' : 'https://docs.dify.ai/v/zh-hans/user-agreement/privacy-policy'} target='_blank' rel='noopener noreferrer'>Privacy Policy</Link>,
<Link href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/user-agreement/terms-of-service' : 'https://docs.dify.ai/v/zh-hans/user-agreement/terms-of-service'} target='_blank' rel='noopener noreferrer'>Terms of Service</Link>
<Link href='https://dify.ai/privacy' target='_blank' rel='noopener noreferrer'>Privacy Policy</Link>,
<Link href='https://dify.ai/terms' target='_blank' rel='noopener noreferrer'>Terms of Service</Link>
</>
}
</div>
......
......@@ -26,7 +26,7 @@ const ModelIcon: FC<ModelIconProps> = ({
return (
<img
alt='model-icon'
src={`${provider.icon_small[language]}?_token=${localStorage.getItem('console_token')}`}
src={`${provider.icon_small[language] || provider.icon_small.en_US}?_token=${localStorage.getItem('console_token')}`}
className={`w-4 h-4 ${className}`}
/>
)
......
......@@ -69,7 +69,7 @@ const Form: FC<FormProps> = ({
<Tooltip popupContent={
// w-[100px] caused problem
<div className=''>
{tooltip[language]}
{tooltip[language] || tooltip.en_US}
</div>
} >
<HelpCircle className='w-3 h-3 text-gray-500' />
......@@ -91,7 +91,7 @@ const Form: FC<FormProps> = ({
return (
<div key={variable} className='py-3'>
<div className='py-2 text-sm text-gray-900'>
{label[language]}
{label[language] || label.en_US}
{
required && (
<span className='ml-1 text-red-500'>*</span>
......@@ -104,7 +104,7 @@ const Form: FC<FormProps> = ({
value={(isShowDefaultValue && ((value[variable] as string) === '' || value[variable] === undefined || value[variable] === null)) ? formSchema.default : value[variable]}
onChange={val => handleFormChange(variable, val)}
validated={validatedSuccess}
placeholder={placeholder?.[language]}
placeholder={placeholder?.[language] || placeholder?.en_US}
disabled={disabed}
type={formSchema.type === FormTypeEnum.textNumber ? 'number' : 'text'}
{...(formSchema.type === FormTypeEnum.textNumber ? { min: (formSchema as CredentialFormSchemaNumberInput).min, max: (formSchema as CredentialFormSchemaNumberInput).max } : {})}
......@@ -132,7 +132,7 @@ const Form: FC<FormProps> = ({
return (
<div key={variable} className='py-3'>
<div className='py-2 text-sm text-gray-900'>
{label[language]}
{label[language] || label.en_US}
{
required && (
<span className='ml-1 text-red-500'>*</span>
......@@ -188,7 +188,7 @@ const Form: FC<FormProps> = ({
return (
<div key={variable} className='py-3'>
<div className='py-2 text-sm text-gray-900'>
{label[language]}
{label[language] || label.en_US}
{
required && (
......@@ -230,7 +230,7 @@ const Form: FC<FormProps> = ({
<div key={variable} className='py-3'>
<div className='flex items-center justify-between py-2 text-sm text-gray-900'>
<div className='flex items-center space-x-2'>
<span>{label[language]}</span>
<span>{label[language] || label.en_US}</span>
{tooltipContent}
</div>
<Radio.Group
......
......@@ -246,12 +246,12 @@ const ModelModal: FC<ModelModalProps> = ({
(provider.help && (provider.help.title || provider.help.url))
? (
<a
href={provider.help?.url[language]}
href={provider.help?.url[language] || provider.help?.url.en_US}
target='_blank' rel='noopener noreferrer'
className='inline-flex items-center text-xs text-primary-600'
onClick={e => !provider.help.url && e.preventDefault()}
>
{provider.help.title?.[language] || provider.help.url[language]}
{provider.help.title?.[language] || provider.help.url[language] || provider.help.title?.en_US || provider.help.url.en_US}
<LinkExternal02 className='ml-1 w-3 h-3' />
</a>
)
......
......@@ -34,7 +34,6 @@ const ModelName: FC<ModelNameProps> = ({
if (!modelItem)
return null
return (
<div
className={`
......@@ -44,9 +43,9 @@ const ModelName: FC<ModelNameProps> = ({
>
<div
className='mr-1 truncate'
title={modelItem.label[language]}
title={modelItem.label[language] || modelItem.label.en_US}
>
{modelItem.label[language]}
{modelItem.label[language] || modelItem.label.en_US}
</div>
{
showModelType && modelItem.model_type && (
......
......@@ -218,16 +218,16 @@ const ParameterItem: FC<ParameterItemProps> = ({
<div className='shrink-0 flex items-center w-[200px]'>
<div
className='mr-0.5 text-[13px] font-medium text-gray-700 truncate'
title={parameterRule.label[language]}
title={parameterRule.label[language] || parameterRule.label.en_US}
>
{parameterRule.label[language]}
{parameterRule.label[language] || parameterRule.label.en_US}
</div>
{
parameterRule.help && (
<Tooltip
selector={`model-parameter-rule-${parameterRule.name}`}
htmlContent={(
<div className='w-[200px] whitespace-pre-wrap'>{parameterRule.help[language]}</div>
<div className='w-[200px] whitespace-pre-wrap'>{parameterRule.help[language] || parameterRule.help.en_US}</div>
)}
>
<HelpCircle className='mr-1.5 w-3.5 h-3.5 text-gray-400' />
......
......@@ -65,7 +65,7 @@ const PopupItem: FC<PopupItemProps> = ({
return (
<div className='mb-1'>
<div className='flex items-center px-3 h-[22px] text-xs font-medium text-gray-500'>
{model.label[language]}
{model.label[language] || model.label.en_US}
</div>
{
model.models.map(modelItem => (
......
......@@ -23,7 +23,22 @@ const Popup: FC<PopupProps> = ({
const language = useLanguage()
const [searchText, setSearchText] = useState('')
const filteredModelList = modelList.filter(model => model.models.filter(modelItem => modelItem.label[language].includes(searchText)).length)
const filteredModelList = modelList.filter(
model => model.models.filter(
(modelItem) => {
if (modelItem.label[language] !== undefined)
return modelItem.label[language].includes(searchText)
let found = false
Object.keys(modelItem.label).forEach((key) => {
if (modelItem.label[key].includes(searchText))
found = true
})
return found
},
).length,
)
return (
<div className='w-[320px] max-h-[480px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg overflow-y-auto'>
......
......@@ -69,7 +69,7 @@ const ProviderCard: FC<ProviderCardProps> = ({
</div>
{
provider.description && (
<div className='mt-1 leading-4 text-xs text-black/[48]'>{provider.description[language]}</div>
<div className='mt-1 leading-4 text-xs text-black/[48]'>{provider.description[language] || provider.description.en_US}</div>
)
}
</div>
......
......@@ -16,7 +16,7 @@ const ProviderIcon: FC<ProviderIconProps> = ({
return (
<img
alt='provider-icon'
src={`${provider.icon_large[language]}?_token=${localStorage.getItem('console_token')}`}
src={`${provider.icon_large[language] || provider.icon_large.en_US}?_token=${localStorage.getItem('console_token')}`}
className={`w-auto h-6 ${className}`}
/>
)
......
......@@ -2,10 +2,9 @@
import type { FC } from 'react'
import React, { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import produce from 'immer'
import { useDebounce, useGetState } from 'ahooks'
import { clone } from 'lodash-es'
import cn from 'classnames'
import produce from 'immer'
import { LinkExternal02, Settings01 } from '../../base/icons/src/vender/line/general'
import type { Credential, CustomCollectionBackend, CustomParamSchema, Emoji } from '../types'
import { AuthHeaderPrefix, AuthType } from '../types'
......@@ -116,14 +115,16 @@ const EditCustomCollectionModal: FC<Props> = ({
const [isShowTestApi, setIsShowTestApi] = useState(false)
const handleSave = () => {
const postData = clone(customCollection)
delete postData.tools
// const postData = clone(customCollection)
const postData = produce(customCollection, (draft) => {
delete draft.tools
if (postData.credentials.auth_type === AuthType.none) {
delete postData.credentials.api_key_header
delete postData.credentials.api_key_header_prefix
delete postData.credentials.api_key_value
if (draft.credentials.auth_type === AuthType.none) {
delete draft.credentials.api_key_header
delete draft.credentials.api_key_header_prefix
delete draft.credentials.api_key_value
}
})
if (isAdd) {
onAdd?.(postData)
......
......@@ -42,6 +42,7 @@ const TestApi: FC<Props> = ({
delete credentials.api_key_value
}
const data = {
provider_name: customCollection.provider,
tool_name: toolName,
credentials,
schema_type: customCollection.schema_type,
......
......@@ -12,7 +12,6 @@ import { IS_CE_EDITION, apiPrefix } from '@/config'
import Button from '@/app/components/base/button'
import { login, oauth } from '@/service/common'
import I18n from '@/context/i18n'
import { LanguagesSupported } from '@/i18n/language'
import { getPurifyHref } from '@/utils'
const validEmailReg = /^[\w\.-]+@([\w-]+\.)+[\w-]{2,}$/
......@@ -282,13 +281,13 @@ const NormalForm = () => {
<Link
className='text-primary-600'
target='_blank' rel='noopener noreferrer'
href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/user-agreement/terms-of-service' : 'https://docs.dify.ai/v/zh-hans/user-agreement/terms-of-service'}
href='https://dify.ai/terms'
>{t('login.tos')}</Link>
&nbsp;&&nbsp;
<Link
className='text-primary-600'
target='_blank' rel='noopener noreferrer'
href={locale !== LanguagesSupported[1] ? 'https://docs.dify.ai/user-agreement/privacy-policy' : 'https://docs.dify.ai/v/zh-hans/user-agreement/privacy-policy'}
href='https://dify.ai/privacy'
>{t('login.pp')}</Link>
</div>
......
const translation = {
title: '注釈',
name: '注釈の返信',
editBy: '{{author}} によって編集された回答',
noData: {
title: '注釈がありません',
description: 'ここではアプリのデバッグ中に注釈を編集したり、一括で注釈をインポートしたりして高品質の応答を行うことができます。',
},
table: {
header: {
question: '質問',
answer: '回答',
createdAt: '作成日時',
hits: 'ヒット数',
actions: 'アクション',
addAnnotation: '注釈を追加',
bulkImport: '一括インポート',
bulkExport: '一括エクスポート',
clearAll: 'すべての注釈をクリア',
},
},
editModal: {
title: '注釈の返信を編集',
queryName: 'ユーザーのクエリ',
answerName: 'ストーリーテラーボット',
yourAnswer: 'あなたの回答',
answerPlaceholder: 'ここに回答を入力してください',
yourQuery: 'あなたのクエリ',
queryPlaceholder: 'ここにクエリを入力してください',
removeThisCache: 'この注釈を削除',
createdAt: '作成日時',
},
addModal: {
title: '注釈の返信を追加',
queryName: '質問',
answerName: '回答',
answerPlaceholder: 'ここに回答を入力してください',
queryPlaceholder: 'ここに質問を入力してください',
createNext: '別の注釈付きの応答を追加',
},
batchModal: {
title: '一括インポート',
csvUploadTitle: 'CSVファイルをここにドラッグ&ドロップするか、',
browse: '参照',
tip: 'CSVファイルは以下の構造に準拠する必要があります:',
question: '質問',
answer: '回答',
contentTitle: 'チャンクの内容',
content: '内容',
template: 'テンプレートをここからダウンロード',
cancel: 'キャンセル',
run: '一括実行',
runError: '一括実行に失敗しました',
processing: '一括処理中',
completed: 'インポートが完了しました',
error: 'インポートエラー',
ok: 'OK',
},
errorMessage: {
answerRequired: '回答は必須です',
queryRequired: '質問は必須です',
},
viewModal: {
annotatedResponse: '注釈の返信',
hitHistory: 'ヒット履歴',
hit: 'ヒット',
hits: 'ヒット数',
noHitHistory: 'ヒット履歴はありません',
},
hitHistoryTable: {
query: 'クエリ',
match: '一致',
response: '応答',
source: 'ソース',
score: 'スコア',
time: '時間',
},
initSetup: {
title: '注釈の返信の初期設定',
configTitle: '注釈の返信の設定',
confirmBtn: '保存して有効にする',
configConfirmBtn: '保存',
},
embeddingModelSwitchTip: '注釈テキストのベクトル化モデルです。モデルを切り替えると再埋め込みが行われ、追加のコストが発生します。',
}
export default translation
const translation = {
apiServer: 'APIサーバー',
apiKey: 'APIキー',
status: 'ステータス',
disabled: '無効',
ok: '稼働中',
copy: 'コピー',
copied: 'コピー済み',
play: '再生',
pause: '一時停止',
playing: '再生中',
merMaind: {
rerender: '再レンダリング',
},
never: 'なし',
apiKeyModal: {
apiSecretKey: 'APIシークレットキー',
apiSecretKeyTips: 'APIの悪用を防ぐために、APIキーを保護してください。フロントエンドのコードで平文として使用しないでください。:)',
createNewSecretKey: '新しいシークレットキーを作成',
secretKey: 'シークレットキー',
created: '作成日時',
lastUsed: '最終使用日時',
generateTips: 'このキーを安全でアクセス可能な場所に保管してください。',
},
actionMsg: {
deleteConfirmTitle: 'このシークレットキーを削除しますか?',
deleteConfirmTips: 'この操作は元に戻すことはできません。',
ok: 'OK',
},
completionMode: {
title: '補完アプリAPI',
info: '記事、要約、翻訳などの高品質なテキスト生成には、ユーザーの入力を使用した補完メッセージAPIを使用します。テキスト生成は、Dify Prompt Engineeringで設定されたモデルパラメータとプロンプトテンプレートに依存しています。',
createCompletionApi: '補完メッセージの作成',
createCompletionApiTip: '質疑応答モードをサポートするために、補完メッセージを作成します。',
inputsTips: '(オプション)Prompt Engの変数に対応するキーと値のペアとしてユーザー入力フィールドを提供します。キーは変数名で、値はパラメータの値です。フィールドのタイプがSelectの場合、送信される値は事前に設定された選択肢のいずれかである必要があります。',
queryTips: 'ユーザーの入力テキスト内容。',
blocking: 'ブロッキングタイプで、実行が完了して結果が返されるまで待機します。(処理が長い場合、リクエストは中断される場合があります)',
streaming: 'ストリーミングの返却。SSE(Server-Sent Events)に基づいたストリーミングの返却の実装。',
messageFeedbackApi: 'メッセージフィードバック(いいね)',
messageFeedbackApiTip: 'エンドユーザーの代わりに受信したメッセージを「いいね」または「いいね」で評価します。このデータはログ&注釈ページで表示され、将来のモデルの微調整に使用されます。',
messageIDTip: 'メッセージID',
ratingTip: 'いいねまたはいいね、nullは元に戻す',
parametersApi: 'アプリケーションパラメータ情報の取得',
parametersApiTip: '変数名、フィールド名、タイプ、デフォルト値を含む設定済みの入力パラメータを取得します。通常、これらのフィールドをフォームに表示したり、クライアントの読み込み後にデフォルト値を入力したりするために使用されます。',
},
chatMode: {
title: 'チャットアプリAPI',
info: '質疑応答形式を使用した多目的の対話型アプリケーションには、チャットメッセージAPIを呼び出して対話を開始します。返されたconversation_idを渡すことで、継続的な会話を維持します。応答パラメータとテンプレートは、Dify Prompt Engの設定に依存します。',
createChatApi: 'チャットメッセージの作成',
createChatApiTip: '新しい会話メッセージを作成するか、既存の対話を継続します。',
inputsTips: '(オプション)Prompt Engの変数に対応するキーと値のペアとしてユーザー入力フィールドを提供します。キーは変数名で、値はパラメータの値です。フィールドのタイプがSelectの場合、送信される値は事前に設定された選択肢のいずれかである必要があります。',
queryTips: 'ユーザーの入力/質問内容',
blocking: 'ブロッキングタイプで、実行が完了して結果が返されるまで待機します。(処理が長い場合、リクエストは中断される場合があります)',
streaming: 'ストリーミングの返却。SSE(Server-Sent Events)に基づいたストリーミングの返却の実装。',
conversationIdTip: '(オプション)会話ID:初回の会話の場合は空白のままにしておき、継続する場合はコンテキストからconversation_idを渡します。',
messageFeedbackApi: 'メッセージ端末ユーザーフィードバック、いいね',
messageFeedbackApiTip: 'エンドユーザーの代わりに受信したメッセージを「いいね」または「いいね」で評価します。このデータはログ&注釈ページで表示され、将来のモデルの微調整に使用されます。',
messageIDTip: 'メッセージID',
ratingTip: 'いいねまたはいいね、nullは元に戻す',
chatMsgHistoryApi: 'チャット履歴メッセージの取得',
chatMsgHistoryApiTip: '最初のページは最新の「limit」バーを返します。逆順です。',
chatMsgHistoryConversationIdTip: '会話ID',
chatMsgHistoryFirstId: '現在のページの最初のチャットレコードのID。デフォルトはなし。',
chatMsgHistoryLimit: '1回のリクエストで返されるチャットの数',
conversationsListApi: '会話リストの取得',
conversationsListApiTip: '現在のユーザーのセッションリストを取得します。デフォルトでは、最後の20のセッションが返されます。',
conversationsListFirstIdTip: '現在のページの最後のレコードのID、デフォルトはなし。',
conversationsListLimitTip: '1回のリクエストで返されるチャットの数',
conversationRenamingApi: '会話の名前変更',
conversationRenamingApiTip: '会話の名前を変更します。名前はマルチセッションクライアントインターフェースに表示されます。',
conversationRenamingNameTip: '新しい名前',
parametersApi: 'アプリケーションパラメータ情報の取得',
parametersApiTip: '変数名、フィールド名、タイプ、デフォルト値を含む設定済みの入力パラメータを取得します。通常、これらのフィールドをフォームに表示したり、クライアントの読み込み後にデフォルト値を入力したりするために使用されます。',
},
develop: {
requestBody: 'リクエストボディ',
pathParams: 'パスパラメータ',
query: 'クエリ',
},
}
export default translation
This diff is collapsed.
const translation = {
title: 'ログ',
description: 'ログは、アプリケーションの実行状態を記録します。ユーザーの入力やAIの応答などが含まれます。',
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
table: {
header: {
time: '時間',
endUser: 'エンドユーザー',
input: '入力',
output: '出力',
summary: 'タイトル',
messageCount: 'メッセージ数',
userRate: 'ユーザー評価',
adminRate: 'オペレータ評価',
},
pagination: {
previous: '前へ',
next: '次へ',
},
empty: {
noChat: 'まだ会話がありません',
noOutput: '出力なし',
element: {
title: '誰かいますか?',
content: 'ここではエンドユーザーとAIアプリケーションの相互作用を観察し、注釈を付けることでAIの精度を継続的に向上させることができます。自分自身でWebアプリを<shareLink>共有</shareLink>したり<testLink>テスト</testLink>したりして、このページに戻ってください。',
},
},
},
detail: {
time: '時間',
conversationId: '会話ID',
promptTemplate: 'プロンプトテンプレート',
promptTemplateBeforeChat: 'チャット前のプロンプトテンプレート · システムメッセージとして',
annotationTip: '{{user}}による改善',
timeConsuming: '',
second: '秒',
tokenCost: 'トークン消費',
loading: '読み込み中',
operation: {
like: 'いいね',
dislike: 'いまいち',
addAnnotation: '改善を追加',
editAnnotation: '改善を編集',
annotationPlaceholder: 'AIが返答することを期待する回答を入力してください。これはモデルの微調整やテキスト生成品質の継続的な改善に使用されます。',
},
variables: '変数',
uploadImages: 'アップロードされた画像',
},
filter: {
period: {
today: '今日',
last7days: '過去7日間',
last4weeks: '過去4週間',
last3months: '過去3ヶ月',
last12months: '過去12ヶ月',
monthToDate: '今月まで',
quarterToDate: '四半期まで',
yearToDate: '今年まで',
allTime: 'すべての期間',
},
annotation: {
all: 'すべて',
annotated: '注釈付きの改善({{count}}件)',
not_annotated: '注釈なし',
},
},
}
export default translation
const translation = {
welcome: {
firstStepTip: '始めるには、',
enterKeyTip: '以下にOpenAI APIキーを入力してください',
getKeyTip: 'OpenAIダッシュボードからAPIキーを取得してください',
placeholder: 'OpenAI APIキー(例:sk-xxxx)',
},
apiKeyInfo: {
cloud: {
trial: {
title: '{{providerName}}のトライアルクオータを使用しています。',
description: 'トライアルクオータはテスト用に提供されています。トライアルクオータの使用回数が尽きる前に、独自のモデルプロバイダを設定するか、追加のクオータを購入してください。',
},
exhausted: {
title: 'トライアルクオータが使い果たされました。APIキーを設定してください。',
description: 'トライアルクオータが使い果たされました。独自のモデルプロバイダを設定するか、追加のクオータを購入してください。',
},
},
selfHost: {
title: {
row1: '始めるには、',
row2: 'まずモデルプロバイダを設定してください。',
},
},
callTimes: '呼び出し回数',
usedToken: '使用済みトークン',
setAPIBtn: 'モデルプロバイダの設定へ',
tryCloud: 'または無料クオートでDifyのクラウドバージョンを試してみてください',
},
overview: {
title: '概要',
appInfo: {
explanation: '使いやすいAI Webアプリ',
accessibleAddress: '公開URL',
preview: 'プレビュー',
regenerate: '再生成',
preUseReminder: '続行する前にWebアプリを有効にしてください。',
settings: {
entry: '設定',
title: 'Webアプリの設定',
webName: 'Webアプリ名',
webDesc: 'Webアプリの説明',
webDescTip: 'このテキストはクライアント側に表示され、アプリの使用方法に関する基本的なガイダンスを提供します。',
webDescPlaceholder: 'Webアプリの説明を入力してください',
language: '言語',
more: {
entry: '詳細設定を表示',
copyright: '著作権',
copyRightPlaceholder: '著作者または組織の名前を入力してください',
privacyPolicy: 'プライバシーポリシー',
privacyPolicyPlaceholder: 'プライバシーポリシーリンクを入力してください',
privacyPolicyTip: '訪問者がアプリが収集するデータを理解するのに役立ちます。Difyの<privacyPolicyLink>プライバシーポリシー</privacyPolicyLink>を参照してください。',
},
},
embedded: {
entry: '埋め込み',
title: 'ウェブサイトに埋め込む',
explanation: 'チャットアプリをウェブサイトに埋め込む方法を選択してください。',
iframe: 'ウェブサイトの任意の場所にチャットアプリを追加するには、このiframeをHTMLコードに追加してください。',
scripts: 'ウェブサイトの右下にチャットアプリを追加するには、このコードをHTMLに追加してください。',
chromePlugin: 'Dify Chatbot Chrome拡張機能をインストール',
copied: 'コピー済み',
copy: 'コピー',
},
qrcode: {
title: '共有用QRコード',
scan: 'アプリを共有するためにスキャン',
download: 'QRコードをダウンロード',
},
customize: {
way: '方法',
entry: 'カスタマイズ',
title: 'AI Webアプリのカスタマイズ',
explanation: 'シナリオとスタイルのニーズに合わせてWebアプリのフロントエンドをカスタマイズできます。',
way1: {
name: 'クライアントコードをフォークして変更し、Vercelにデプロイする(推奨)',
step1: 'クライアントコードをフォークして変更する',
step1Tip: 'ここをクリックしてソースコードをGitHubアカウントにフォークし、コードを変更してください',
step1Operation: 'Dify-WebClient',
step2: 'Vercelにデプロイする',
step2Tip: 'ここをクリックしてリポジトリをVercelにインポートし、デプロイしてください',
step2Operation: 'リポジトリのインポート',
step3: '環境変数を設定する',
step3Tip: 'Vercelに以下の環境変数を追加してください',
},
way2: {
name: 'APIを呼び出すためのクライアントサイドコードを記述し、サーバーにデプロイする',
operation: 'ドキュメント',
},
},
},
apiInfo: {
title: 'バックエンドサービスAPI',
explanation: 'アプリケーションに簡単に統合できます',
accessibleAddress: 'サービスAPIエンドポイント',
doc: 'APIリファレンス',
},
status: {
running: 'サービス中',
disable: '無効化',
},
},
analysis: {
title: '分析',
ms: 'ミリ秒',
tokenPS: 'トークン/秒',
totalMessages: {
title: '総メッセージ数',
explanation: 'AIとのやり取りのうち、プロンプトのエンジニアリングやデバッグを除いた日次の相互作用数です。',
},
activeUsers: {
title: 'アクティブユーザー',
explanation: 'AIとのQ&Aに参加しているユニークなユーザー数です。プロンプトのエンジニアリングやデバッグを除きます。',
},
tokenUsage: {
title: 'トークン使用量',
explanation: 'アプリケーションの言語モデルの日次トークン使用量を反映し、コスト管理の目的に役立ちます。',
consumed: '使用済み',
},
avgSessionInteractions: {
title: '平均セッション相互作用数',
explanation: '会話ベースのアプリケーションの連続したユーザーとAIのコミュニケーション数です。',
},
userSatisfactionRate: {
title: 'ユーザー満足率',
explanation: '1,000メッセージあたりの「いいね」の数です。これは、ユーザーが非常に満足している回答の割合を示します。',
},
avgResponseTime: {
title: '平均応答時間',
explanation: 'AIの処理/応答にかかる時間(ミリ秒)です。テキストベースのアプリケーションに適しています。',
},
tps: {
title: 'トークン出力速度',
explanation: 'LLMのパフォーマンスを測定します。リクエストの開始から出力の完了までのLLMのトークン出力速度をカウントします。',
},
},
}
export default translation
const translation = {
createApp: '新しいアプリを作成する',
types: {
all: 'すべて',
assistant: 'アシスタント',
completion: '補完',
},
modes: {
completion: 'テキスト生成',
chat: '基本アシスタント',
},
createFromConfigFile: '設定ファイルからアプリを作成する',
deleteAppConfirmTitle: 'このアプリを削除しますか?',
deleteAppConfirmContent:
'アプリの削除は元に戻せません。ユーザーはアプリにアクセスできなくなり、プロンプトの設定とログは永久に削除されます。',
appDeleted: 'アプリが削除されました',
appDeleteFailed: 'アプリの削除に失敗しました',
join: 'コミュニティに参加する',
communityIntro:
'チームメンバーや貢献者、開発者とさまざまなチャンネルでディスカッションを行います。',
roadmap: 'ロードマップを見る',
appNamePlaceholder: 'アプリの名前を入力してください',
newApp: {
startToCreate: '新しいアプリを作成しましょう',
captionName: 'アプリアイコンと名前',
captionAppType: 'どのタイプのアプリを作成しますか?',
previewDemo: 'デモをプレビューする',
chatApp: 'アシスタント',
chatAppIntro:
'チャットベースのアプリケーションを構築したいです。このアプリは質問と回答の形式を使用し、複数のラウンドの連続した会話が可能です。',
agentAssistant: '新しいエージェントアシスタント',
completeApp: 'テキスト生成',
completeAppIntro:
'プロンプトに基づいて高品質のテキストを生成するアプリケーションを作成したいです。記事、要約、翻訳などの生成が可能です。',
showTemplates: 'テンプレートから選択したいです',
hideTemplates: 'モード選択に戻る',
Create: '作成',
Cancel: 'キャンセル',
nameNotEmpty: '名前は空にできません',
appTemplateNotSelected: 'テンプレートを選択してください',
appTypeRequired: 'アプリのタイプを選択してください',
appCreated: 'アプリが作成されました',
appCreateFailed: 'アプリの作成に失敗しました',
},
editApp: {
startToEdit: 'アプリを編集する',
},
emoji: {
ok: 'OK',
cancel: 'キャンセル',
},
}
export default translation
const translation = {
currentPlan: '現在のプラン',
upgradeBtn: {
plain: 'プランをアップグレード',
encourage: '今すぐアップグレード',
encourageShort: 'アップグレード',
},
viewBilling: '請求とサブスクリプションの管理',
buyPermissionDeniedTip: 'サブスクリプションするには、エンタープライズ管理者に連絡してください',
plansCommon: {
title: 'あなたに合ったプランを選択してください',
yearlyTip: '年間購読で2か月無料!',
mostPopular: '最も人気のある',
planRange: {
monthly: '月額',
yearly: '年額',
},
month: '月',
year: '年',
save: '節約 ',
free: '無料',
currentPlan: '現在のプラン',
contractSales: '営業に連絡する',
contractOwner: 'チームマネージャーに連絡する',
startForFree: '無料で始める',
getStartedWith: '始める ',
contactSales: '営業に連絡する',
talkToSales: '営業と話す',
modelProviders: 'モデルプロバイダー',
teamMembers: 'チームメンバー',
buildApps: 'アプリを作成する',
vectorSpace: 'ベクトルスペース',
vectorSpaceBillingTooltip: '1MBあたり約120万文字のベクトル化データを保存できます(OpenAI Embeddingsを使用して推定され、モデルによって異なります)。',
vectorSpaceTooltip: 'ベクトルスペースは、LLMがデータを理解するために必要な長期記憶システムです。',
documentProcessingPriority: 'ドキュメント処理の優先度',
documentProcessingPriorityTip: 'より高いドキュメント処理の優先度をご希望の場合は、プランをアップグレードしてください。',
documentProcessingPriorityUpgrade: 'より高い精度と高速な速度でデータを処理します。',
priority: {
'standard': '標準',
'priority': '優先',
'top-priority': '最優先',
},
logsHistory: 'ログ履歴',
customTools: 'カスタムツール',
unavailable: '利用不可',
days: '日',
unlimited: '無制限',
support: 'サポート',
supportItems: {
communityForums: 'コミュニティフォーラム',
emailSupport: 'メールサポート',
priorityEmail: '優先メール&チャットサポート',
logoChange: 'ロゴ変更',
SSOAuthentication: 'SSO認証',
personalizedSupport: '個別サポート',
dedicatedAPISupport: '専用APIサポート',
customIntegration: 'カスタム統合とサポート',
ragAPIRequest: 'RAG APIリクエスト',
agentMode: 'エージェントモード',
workflow: 'ワークフロー',
},
comingSoon: '近日公開',
member: 'メンバー',
memberAfter: 'メンバー',
messageRequest: {
title: 'メッセージクレジット',
tooltip: 'OpenAIモデルを使用したさまざまなプランのメッセージ呼び出しクォータ(gpt4を除く)。制限を超えるメッセージはOpenAI APIキーを使用します。',
},
annotatedResponse: {
title: '注釈クォータ制限',
tooltip: '手動での編集と応答の注釈付けにより、アプリのカスタマイズ可能な高品質な質問応答機能が提供されます(チャットアプリのみ適用)。',
},
ragAPIRequestTooltip: 'Difyのナレッジベース処理機能のみを呼び出すAPI呼び出しの数を指します。',
receiptInfo: 'チームオーナーとチーム管理者のみが購読および請求情報を表示できます',
},
plans: {
sandbox: {
name: 'サンドボックス',
description: 'GPTの無料トライアル200回',
includesTitle: '含まれるもの:',
},
professional: {
name: 'プロフェッショナル',
description: '個人や小規模チーム向けにより多くのパワーを手頃な価格で提供します。',
includesTitle: '無料プランに加えて、次も含まれます:',
},
team: {
name: 'チーム',
description: '制限なく協力し、最高のパフォーマンスを楽しむ。',
includesTitle: 'プロフェッショナルプランに加えて、次も含まれます:',
},
enterprise: {
name: 'エンタープライズ',
description: '大規模なミッションクリティカルシステムのためのフル機能とサポートを提供します。',
includesTitle: 'チームプランに加えて、次も含まれます:',
},
},
vectorSpace: {
fullTip: 'ベクトルスペースがいっぱいです。',
fullSolution: 'より多くのスペースを得るためにプランをアップグレードしてください。',
},
apps: {
fullTipLine1: 'より多くのアプリを作成するには、',
fullTipLine2: 'プランをアップグレードしてください。',
},
annotatedResponse: {
fullTipLine1: 'より多くの会話を注釈するには、',
fullTipLine2: 'プランをアップグレードしてください。',
quotaTitle: '注釈返信クォータ',
},
}
export default translation
This diff is collapsed.
const translation = {
custom: 'カスタマイズ',
upgradeTip: {
prefix: 'プランをアップグレードして',
suffix: 'ブランドをカスタマイズしましょう。',
},
webapp: {
title: 'WebAppブランドのカスタマイズ',
removeBrand: 'Powered by Difyを削除',
changeLogo: 'Powered byブランド画像を変更',
changeLogoTip: '最小サイズ40x40pxのSVGまたはPNG形式',
},
app: {
title: 'アプリヘッダーブランドのカスタマイズ',
changeLogoTip: '最小サイズ80x80pxのSVGまたはPNG形式',
},
upload: 'アップロード',
uploading: 'アップロード中',
uploadedFail: '画像のアップロードに失敗しました。再度アップロードしてください。',
change: '変更',
apply: '適用',
restore: 'デフォルトに戻す',
customize: {
contactUs: 'お問い合わせ',
prefix: 'アプリ内のブランドロゴをカスタマイズするには、',
suffix: 'エンタープライズ版にアップグレードしてください。',
},
}
export default translation
const translation = {
steps: {
header: {
creation: 'ナレッジの作成',
update: 'データの追加',
},
one: 'データソースの選択',
two: 'テキストの前処理とクリーニング',
three: '実行して完了',
},
error: {
unavailable: 'このナレッジは利用できません',
},
stepOne: {
filePreview: 'ファイルプレビュー',
pagePreview: 'ページプレビュー',
dataSourceType: {
file: 'テキストファイルからインポート',
notion: 'Notionから同期',
web: 'ウェブサイトから同期',
},
uploader: {
title: 'テキストファイルをアップロード',
button: 'ファイルをドラッグ&ドロップするか',
browse: '参照',
tip: '{{supportTypes}}をサポートしています。1つあたりの最大サイズは{{size}}MBです。',
validation: {
typeError: 'サポートされていないファイルタイプです',
size: 'ファイルサイズが大きすぎます。最大サイズは{{size}}MBです',
count: '複数のファイルはサポートされていません',
filesNumber: 'バッチアップロードの制限({{filesNumber}}個)に達しました。',
},
cancel: 'キャンセル',
change: '変更',
failed: 'アップロードに失敗しました',
},
notionSyncTitle: 'Notionが接続されていません',
notionSyncTip: 'Notionと同期するには、まずNotionへの接続が必要です。',
connect: '接続する',
button: '次へ',
emptyDatasetCreation: '空のナレッジを作成します',
modal: {
title: '空のナレッジを作成',
tip: '空のナレッジにはドキュメントが含まれず、いつでもドキュメントをアップロードできます。',
input: 'ナレッジ名',
placeholder: '入力してください',
nameNotEmpty: '名前は空にできません',
nameLengthInvaild: '名前は1〜40文字である必要があります',
cancelButton: 'キャンセル',
confirmButton: '作成',
failed: '作成に失敗しました',
},
},
stepTwo: {
segmentation: 'チャンク設定',
auto: '自動',
autoDescription: 'チャンクと前処理ルールを自動的に設定します。初めてのユーザーはこれを選択することをおすすめします。',
custom: 'カスタム',
customDescription: 'チャンクのルール、チャンクの長さ、前処理ルールなどをカスタマイズします。',
separator: 'セグメント識別子',
separatorPlaceholder: '例えば改行(\\\\n)や特殊なセパレータ(例:「***」)',
maxLength: '最大チャンク長',
overlap: 'チャンクのオーバーラップ',
overlapTip: 'チャンクのオーバーラップを設定することで、それらの間の意味的な関連性を維持し、検索効果を向上させることができます。最大チャンクサイズの10%〜25%を設定することをおすすめします。',
overlapCheck: 'チャンクのオーバーラップは最大チャンク長を超えてはいけません',
rules: 'テキストの前処理ルール',
removeExtraSpaces: '連続するスペース、改行、タブを置換する',
removeUrlEmails: 'すべてのURLとメールアドレスを削除する',
removeStopwords: '「a」「an」「the」などのストップワードを削除する',
preview: '確認&プレビュー',
reset: 'リセット',
indexMode: 'インデックスモード',
qualified: '高品質',
recommend: 'おすすめ',
qualifiedTip: 'ユーザーのクエリに対してより高い精度を提供するために、デフォルトのシステム埋め込みインターフェースを呼び出して処理します。',
warning: 'モデルプロバイダのAPIキーを設定してください。',
click: '設定に移動',
economical: '経済的',
economicalTip: 'オフラインのベクトルエンジン、キーワードインデックスなどを使用して、トークンを消費せずに精度を低下させます。',
QATitle: '質問と回答形式でセグメント化',
QATip: 'このオプションを有効にすると、追加のトークンが消費されます',
QALanguage: '使用言語',
emstimateCost: '見積もり',
emstimateSegment: '推定チャンク数',
segmentCount: 'チャンク',
calculating: '計算中...',
fileSource: 'ドキュメントの前処理',
notionSource: 'ページの前処理',
other: 'その他',
fileUnit: 'ファイル',
notionUnit: 'ページ',
lastStep: '最後のステップ',
nextStep: '保存して処理',
save: '保存して処理',
cancel: 'キャンセル',
sideTipTitle: 'なぜチャンクと前処理が必要なのか',
sideTipP1: 'テキストデータを処理する際、チャンクとクリーニングは2つの重要な前処理ステップです。',
sideTipP2: 'セグメンテーションは長いテキストを段落に分割し、モデルがより理解しやすくします。これにより、モデルの結果の品質と関連性が向上します。',
sideTipP3: 'クリーニングは不要な文字や書式を削除し、ナレッジをよりクリーンで解析しやすいものにします。',
sideTipP4: '適切なチャンクとクリーニングはモデルのパフォーマンスを向上させ、より正確で価値のある結果を提供します。',
previewTitle: 'プレビュー',
previewTitleButton: 'プレビュー',
previewButton: 'Q&A形式に切り替える',
previewSwitchTipStart: '現在のチャンクプレビューはテキスト形式です。質問と回答形式のプレビューに切り替えると、',
previewSwitchTipEnd: ' 追加のトークンが消費されます',
characters: '文字',
indexSettedTip: 'インデックス方法を変更するには、',
retrivalSettedTip: 'インデックス方法を変更するには、',
datasetSettingLink: 'ナレッジ設定',
},
stepThree: {
creationTitle: '🎉 ナレッジが作成されました',
creationContent: 'ナレッジの名前は自動的に設定されましたが、いつでも変更できます',
label: 'ナレッジ名',
additionTitle: '🎉 ドキュメントがアップロードされました',
additionP1: 'ドキュメントはナレッジにアップロードされました',
additionP2: '、ナレッジのドキュメントリストで見つけることができます。',
stop: '処理を停止',
resume: '処理を再開',
navTo: 'ドキュメントに移動',
sideTipTitle: '次は何ですか',
sideTipContent: 'ドキュメントのインデックスが完了したら、ナレッジをアプリケーションのコンテキストとして統合することができます。プロンプトオーケストレーションページでコンテキスト設定を見つけることができます。また、独立したChatGPTインデックスプラグインとしてリリースすることもできます。',
modelTitle: '埋め込みを停止してもよろしいですか?',
modelContent: '後で処理を再開する必要がある場合は、中断した場所から続行します。',
modelButtonConfirm: '確認',
modelButtonCancel: 'キャンセル',
},
}
export default translation
This diff is collapsed.
const translation = {
title: '検索テスト',
desc: '与えられたクエリテキストに基づいたナレッジのヒット効果をテストします。',
dateTimeFormat: 'MM/DD/YYYY hh:mm A',
recents: '最近の結果',
table: {
header: {
source: 'ソース',
text: 'テキスト',
time: '時間',
},
},
input: {
title: 'ソーステキスト',
placeholder: 'テキストを入力してください。短い記述文がおすすめです。',
countWarning: '最大200文字まで入力できます。',
indexWarning: '高品質のナレッジのみ。',
testing: 'テスト中',
},
hit: {
title: '検索結果パラグラフ',
emptyTip: '検索テストの結果がここに表示されます。',
},
noRecentTip: '最近のクエリ結果はありません。',
viewChart: 'ベクトルチャートを表示',
}
export default translation
const translation = {
title: 'ナレッジの設定',
desc: 'ここではナレッジのプロパティと動作方法を変更できます。',
form: {
name: 'ナレッジ名',
namePlaceholder: 'ナレッジ名を入力してください',
nameError: '名前は空にできません',
desc: 'ナレッジの説明',
descInfo: 'ナレッジの内容を概説するための明確なテキストの説明を書いてください。この説明は、複数のナレッジから推論を選択する際の基準として使用されます。',
descPlaceholder: 'このナレッジに含まれる内容を説明してください。詳細な説明は、AIがナレッジの内容にタイムリーにアクセスできるようにします。空の場合、Difyはデフォルトのヒット戦略を使用します。',
descWrite: '良いナレッジの説明の書き方を学ぶ。',
permissions: '権限',
permissionsOnlyMe: '自分のみ',
permissionsAllMember: 'すべてのチームメンバー',
indexMethod: 'インデックス方法',
indexMethodHighQuality: '高品質',
indexMethodHighQualityTip: 'ユーザーのクエリ時により高い精度を提供するために、OpenAIの埋め込みインターフェースを呼び出して処理します。',
indexMethodEconomy: '経済的',
indexMethodEconomyTip: 'オフラインのベクトルエンジン、キーワードインデックスなどを使用して精度を低下させることなく、トークンを消費せずに処理します。',
embeddingModel: '埋め込みモデル',
embeddingModelTip: '埋め込みモデルを変更するには、',
embeddingModelTipLink: '設定',
retrievalSetting: {
title: '検索設定',
learnMore: '詳細を学ぶ',
description: ' 検索方法についての詳細',
longDescription: ' 検索方法についての詳細については、いつでもナレッジの設定で変更できます。',
},
save: '保存',
},
}
export default translation
const translation = {
knowledge: '知識',
documentCount: ' ドキュメント',
wordCount: 'k 単語',
appCount: ' リンクされたアプリ',
createDataset: '知識を作成',
createDatasetIntro: '独自のテキストデータをインポートするか、LLMコンテキストの強化のためにWebhookを介してリアルタイムでデータを書き込むことができます。',
deleteDatasetConfirmTitle: 'この知識を削除しますか?',
deleteDatasetConfirmContent:
'知識を削除すると元に戻すことはできません。ユーザーはもはやあなたの知識にアクセスできず、すべてのプロンプトの設定とログが永久に削除されます。',
datasetDeleted: '知識が削除されました',
datasetDeleteFailed: '知識の削除に失敗しました',
didYouKnow: 'ご存知ですか?',
intro1: '知識はDifyアプリケーションに統合することができます',
intro2: 'コンテキストとして',
intro3: '、',
intro4: 'または',
intro5: '作成することができます',
intro6: '単体のChatGPTインデックスプラグインとして公開するために',
unavailable: '利用不可',
unavailableTip: '埋め込みモデルが利用できません。デフォルトの埋め込みモデルを設定する必要があります',
datasets: '知識',
datasetsApi: 'API',
retrieval: {
semantic_search: {
title: 'ベクトル検索',
description: 'クエリの埋め込みを生成し、そのベクトル表現に最も類似したテキストチャンクを検索します。',
},
full_text_search: {
title: '全文検索',
description: 'ドキュメント内のすべての用語をインデックス化し、ユーザーが任意の用語を検索してそれに関連するテキストチャンクを取得できるようにします。',
},
hybrid_search: {
title: 'ハイブリッド検索',
description: '全文検索とベクトル検索を同時に実行し、ユーザーのクエリに最適なマッチを選択するために再ランク付けを行います。再ランクモデルAPIの設定が必要です。',
recommend: 'おすすめ',
},
invertedIndex: {
title: '逆インデックス',
description: '効率的な検索に使用される構造です。各用語が含まれるドキュメントまたはWebページを指すように、用語ごとに整理されています。',
},
change: '変更',
changeRetrievalMethod: '検索方法の変更',
},
}
export default translation
const translation = {
title: '探索する',
sidebar: {
discovery: '探索',
chat: 'チャット',
workspace: 'ワークスペース',
action: {
pin: 'ピン留め',
unpin: 'ピン留め解除',
rename: '名前変更',
delete: '削除',
},
delete: {
title: 'アプリを削除',
content: 'このアプリを削除してもよろしいですか?',
},
},
apps: {
title: 'Difyによるアプリの探索',
description: 'これらのテンプレートアプリを即座に使用するか、テンプレートに基づいて独自のアプリをカスタマイズしてください。',
allCategories: 'すべてのカテゴリ',
},
appCard: {
addToWorkspace: 'ワークスペースに追加',
customize: 'カスタマイズ',
},
appCustomize: {
title: '{{name}}からアプリを作成',
subTitle: 'アプリアイコンと名前',
nameRequired: 'アプリ名は必須です',
},
category: {
Assistant: 'アシスタント',
Writing: '執筆',
Translate: '翻訳',
Programming: 'プログラミング',
HR: '人事',
},
}
export default translation
const translation = {
}
export default translation
const translation = {
pageTitle: 'はじめましょう!👋',
welcome: 'Difyへようこそ。続行するにはログインしてください。',
email: 'メールアドレス',
emailPlaceholder: 'メールアドレスを入力してください',
password: 'パスワード',
passwordPlaceholder: 'パスワードを入力してください',
name: 'ユーザー名',
namePlaceholder: 'ユーザー名を入力してください',
forget: 'パスワードをお忘れですか?',
signBtn: 'サインイン',
installBtn: 'セットアップ',
setAdminAccount: '管理者アカウントの設定',
setAdminAccountDesc: 'アプリケーションの作成やLLMプロバイダの管理など、管理者アカウントの最大権限を設定します。',
createAndSignIn: '作成してサインイン',
oneMoreStep: 'あと一歩',
createSample: 'この情報を基に、サンプルアプリケーションを作成します',
invitationCode: '招待コード',
invitationCodePlaceholder: '招待コードを入力してください',
interfaceLanguage: 'インターフェース言語',
timezone: 'タイムゾーン',
go: 'Difyへ移動',
sendUsMail: '自己紹介をメールで送信し、招待リクエストを処理します。',
acceptPP: 'プライバシーポリシーを読み、同意します',
reset: 'パスワードをリセットするには、次のコマンドを実行してください',
withGitHub: 'GitHubで続行',
withGoogle: 'Googleで続行',
rightTitle: 'LLMのフルポテンシャルを解き放つ',
rightDesc: '魅力的で操作可能で改善可能なAIアプリケーションを簡単に構築します。',
tos: '利用規約',
pp: 'プライバシーポリシー',
tosDesc: 'サインアップすることで、以下に同意するものとします',
donthave: 'お持ちでない場合',
invalidInvitationCode: '無効な招待コード',
accountAlreadyInited: 'アカウントは既に初期化されています',
error: {
emailEmpty: 'メールアドレスは必須です',
emailInValid: '有効なメールアドレスを入力してください',
nameEmpty: '名前は必須です',
passwordEmpty: 'パスワードは必須です',
passwordInvalid: 'パスワードは文字と数字を含み、長さは8以上である必要があります',
},
license: {
tip: 'Dify Community Editionを開始する前に、GitHubの',
link: 'オープンソースライセンス',
},
join: '参加する',
joinTipStart: 'あなたを招待します',
joinTipEnd: 'チームに参加する',
invalid: 'リンクの有効期限が切れています',
explore: 'Difyを探索する',
activatedTipStart: 'あなたは',
activatedTipEnd: 'チームに参加しました',
activated: '今すぐサインイン',
adminInitPassword: '管理者初期化パスワード',
validate: '検証',
}
export default translation
const translation = {
}
export default translation
const translation = {
common: {
welcome: '利用していただきありがとうございます',
appUnavailable: 'アプリが利用できません',
appUnkonwError: 'アプリが利用できません',
},
chat: {
newChat: '新しいチャット',
pinnedTitle: 'ピン留めされた',
unpinnedTitle: 'チャット',
newChatDefaultName: '新しい会話',
resetChat: '会話をリセット',
powerBy: 'Powered by',
prompt: 'プロンプト',
privatePromptConfigTitle: '会話の設定',
publicPromptConfigTitle: '初期プロンプト',
configStatusDes: '開始前に、会話の設定を変更できます',
configDisabled:
'前回のセッションの設定がこのセッションで使用されました。',
startChat: 'チャットを開始',
privacyPolicyLeft:
'アプリ開発者が提供する',
privacyPolicyMiddle:
'プライバシーポリシー',
privacyPolicyRight:
'をお読みください。',
deleteConversation: {
title: '会話を削除する',
content: 'この会話を削除してもよろしいですか?',
},
tryToSolve: '解決しようとしています',
temporarySystemIssue: '申し訳ありません、一時的なシステムの問題が発生しました。',
},
generation: {
tabs: {
create: '一度だけ実行',
batch: '一括実行',
saved: '保存済み',
},
savedNoData: {
title: 'まだ結果が保存されていません!',
description: 'コンテンツの生成を開始し、保存された結果をこちらで見つけてください。',
startCreateContent: 'コンテンツの作成を開始',
},
title: 'AI Completion',
queryTitle: 'コンテンツのクエリ',
completionResult: 'Completion 結果',
queryPlaceholder: 'クエリコンテンツを書いてください...',
run: '実行',
copy: 'コピー',
resultTitle: 'AI Completion',
noData: 'AIはここで必要なものを提供します。',
csvUploadTitle: 'CSVファイルをここにドラッグアンドドロップするか、',
browse: '参照',
csvStructureTitle: 'CSVファイルは以下の構造に準拠する必要があります:',
downloadTemplate: 'こちらからテンプレートをダウンロード',
field: 'フィールド',
batchFailed: {
info: '{{num}} 回の実行が失敗しました',
retry: '再試行',
outputPlaceholder: '出力コンテンツなし',
},
errorMsg: {
empty: 'アップロードされたファイルにコンテンツを入力してください。',
fileStructNotMatch: 'アップロードされたCSVファイルが構造と一致しません。',
emptyLine: '行 {{rowIndex}} が空です',
invalidLine: '行 {{rowIndex}}: {{varName}} の値は空にできません',
moreThanMaxLengthLine: '行 {{rowIndex}}: {{varName}} の値は {{maxLength}} 文字を超えることはできません',
atLeastOne: 'アップロードされたファイルには少なくとも1行の入力が必要です。',
},
},
}
export default translation
const translation = {
title: 'ツール',
createCustomTool: 'カスタムツールを作成する',
type: {
all: 'すべて',
builtIn: '組み込み',
custom: 'カスタム',
},
contribute: {
line1: '私は',
line2: 'Difyへのツールの貢献に興味があります。',
viewGuide: 'ガイドを見る',
},
author: '著者',
auth: {
unauthorized: '認証する',
authorized: '認証済み',
setup: '使用するための認証を設定する',
setupModalTitle: '認証の設定',
setupModalTitleDescription: '資格情報を構成した後、ワークスペース内のすべてのメンバーがアプリケーションのオーケストレーション時にこのツールを使用できます。',
},
includeToolNum: '{{num}}個のツールが含まれています',
addTool: 'ツールを追加する',
createTool: {
title: 'カスタムツールを作成する',
editAction: '設定',
editTitle: 'カスタムツールを編集する',
name: '名前',
toolNamePlaceHolder: 'ツール名を入力してください',
schema: 'スキーマ',
schemaPlaceHolder: 'ここにOpenAPIスキーマを入力してください',
viewSchemaSpec: 'OpenAPI-Swagger仕様を表示する',
importFromUrl: 'URLからインポートする',
importFromUrlPlaceHolder: 'https://...',
urlError: '有効なURLを入力してください',
examples: '例',
exampleOptions: {
json: '天気(JSON)',
yaml: 'ペットストア(YAML)',
blankTemplate: '空白テンプレート',
},
availableTools: {
title: '利用可能なツール',
name: '名前',
description: '説明',
method: 'メソッド',
path: 'パス',
action: 'アクション',
test: 'テスト',
},
authMethod: {
title: '認証方法',
type: '認証タイプ',
keyTooltip: 'HTTPヘッダーキー。アイデアがない場合は "Authorization" として残しておいてもかまいません。またはカスタム値に設定できます。',
types: {
none: 'なし',
api_key: 'APIキー',
apiKeyPlaceholder: 'APIキーのHTTPヘッダー名',
apiValuePlaceholder: 'APIキーを入力してください',
},
key: 'キー',
value: '値',
},
authHeaderPrefix: {
title: '認証タイプ',
types: {
basic: 'ベーシック',
bearer: 'ベアラー',
custom: 'カスタム',
},
},
privacyPolicy: 'プライバシーポリシー',
privacyPolicyPlaceholder: 'プライバシーポリシーを入力してください',
},
test: {
title: 'テスト',
parametersValue: 'パラメーター&値',
parameters: 'パラメーター',
value: '値',
testResult: 'テスト結果',
testResultPlaceholder: 'ここにテスト結果が表示されます',
},
thought: {
using: '使用中',
used: '使用済み',
requestTitle: 'リクエスト先',
responseTitle: 'レスポンス先',
},
setBuiltInTools: {
info: '情報',
setting: '設定',
toolDescription: 'ツールの説明',
parameters: 'パラメーター',
string: '文字列',
number: '数',
required: '必須',
infoAndSetting: '情報と設定',
},
noCustomTool: {
title: 'カスタムツールがありません!',
content: 'AIアプリを構築するためのカスタムツールをここで追加および管理します。',
createTool: 'ツールを作成する',
},
noSearchRes: {
title: '申し訳ありません、結果がありません!',
content: '検索に一致するツールが見つかりませんでした。',
reset: '検索をリセット',
},
builtInPromptTitle: 'プロンプト',
toolRemoved: 'ツールが削除されました',
notAuthorized: 'ツールが認可されていません',
howToGet: '取得方法',
}
export default translation
......@@ -59,7 +59,7 @@ export const languages = [
value: 'ja-JP',
name: '日本語(日本)',
example: 'こんにちは、Dify!',
supported: false,
supported: true,
},
{
value: 'ko-KR',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment