From 6dc62334d63a305453818c53a6d207ce64fae766 Mon Sep 17 00:00:00 2001 From: orangeclk Date: Sun, 24 Mar 2024 12:06:20 +0800 Subject: [PATCH 01/18] doc: model schema document fix and wording about the model price parameter (#2944) --- api/core/model_runtime/docs/en_US/schema.md | 2 +- api/core/model_runtime/docs/zh_Hans/schema.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/model_runtime/docs/en_US/schema.md b/api/core/model_runtime/docs/en_US/schema.md index 61cd2c32d4..68f66330cd 100644 --- a/api/core/model_runtime/docs/en_US/schema.md +++ b/api/core/model_runtime/docs/en_US/schema.md @@ -147,7 +147,7 @@ - `input` (float) Input price, i.e., Prompt price - `output` (float) Output price, i.e., returned content price -- `unit` (float) Pricing unit, e.g., per 100K price is `0.000001` +- `unit` (float) Pricing unit, e.g., if the price is meausred in 1M tokens, the corresponding token amount for the unit price is `0.000001`. - `currency` (string) Currency unit ### ProviderCredentialSchema diff --git a/api/core/model_runtime/docs/zh_Hans/schema.md b/api/core/model_runtime/docs/zh_Hans/schema.md index 55202a1a80..fd672993bb 100644 --- a/api/core/model_runtime/docs/zh_Hans/schema.md +++ b/api/core/model_runtime/docs/zh_Hans/schema.md @@ -149,7 +149,7 @@ - `input` (float) 输入单价,即 Prompt 单价 - `output` (float) 输出单价,即返回内容单价 -- `unit` (float) 价格单位,如:每 100K 的单价为 `0.000001` +- `unit` (float) 价格单位,如以 1M tokens 计价,则单价对应的单位 token 数为 `0.000001` - `currency` (string) 货币单位 ### ProviderCredentialSchema From 46ccfda4932f46a0ed0a935c66e6d8c0e7d074d9 Mon Sep 17 00:00:00 2001 From: Nanguan Lin Date: Sun, 24 Mar 2024 12:10:13 +0800 Subject: [PATCH 02/18] fix: invalid i18 link in README (#2947) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 557ac86770..207f312946 100644 --- a/README.md +++ b/README.md @@ -128,7 +128,7 @@ At the same time, please consider supporting Dify by sharing it on social media ### Translations -We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README_EN.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/AhzKf7dNgk). +We are looking for contributors to help with translating Dify to languages other than Mandarin or English. If you are interested in helping, please see the [i18n README](https://github.com/langgenius/dify/blob/main/web/i18n/README.md) for more information, and leave us a comment in the `global-users` channel of our [Discord Community Server](https://discord.gg/AhzKf7dNgk). ## Community & Support From c534d95972eaad24a5c48a12267dbd7d2a0ba20e Mon Sep 17 00:00:00 2001 From: orangeclk Date: Sun, 24 Mar 2024 12:10:57 +0800 Subject: [PATCH 03/18] fix: yi model price correction (#2946) --- .../model_providers/yi/llm/yi-34b-chat-0205.yaml | 6 +++--- .../model_providers/yi/llm/yi-34b-chat-200k.yaml | 6 +++--- .../model_runtime/model_providers/yi/llm/yi-vl-plus.yaml | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml index 429c646b77..ea3d8f5dce 100644 --- a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml +++ b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-0205.yaml @@ -37,7 +37,7 @@ parameter_rules: zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. pricing: - input: '0.0025' - output: '0.0025' - unit: '0.00001' + input: '2.5' + output: '2.5' + unit: '0.000001' currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml index d0e181d007..d91f984d7f 100644 --- a/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml +++ b/api/core/model_runtime/model_providers/yi/llm/yi-34b-chat-200k.yaml @@ -37,7 +37,7 @@ parameter_rules: zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. pricing: - input: '0.012' - output: '0.012' - unit: '0.00001' + input: '12' + output: '12' + unit: '0.000001' currency: RMB diff --git a/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml b/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml index a6abcc401f..461c68583f 100644 --- a/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml +++ b/api/core/model_runtime/model_providers/yi/llm/yi-vl-plus.yaml @@ -37,7 +37,7 @@ parameter_rules: zh_Hans: 控制生成结果的随机性。数值越小,随机性越弱;数值越大,随机性越强。一般而言,top_p 和 temperature 两个参数选择一个进行调整即可。 en_US: Control the randomness of generated results. The smaller the value, the weaker the randomness; the larger the value, the stronger the randomness. Generally speaking, you can adjust one of the two parameters top_p and temperature. pricing: - input: '0.01' - output: '0.03' - unit: '0.001' - currency: USD + input: '6' + output: '6' + unit: '0.000001' + currency: RMB From 4fb9606361f5cf6c7a953436ded56cebabe7032b Mon Sep 17 00:00:00 2001 From: orangeclk Date: Mon, 25 Mar 2024 10:07:32 +0800 Subject: [PATCH 04/18] fix: max_token default help info improved (#2951) --- api/core/model_runtime/entities/defaults.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/model_runtime/entities/defaults.py b/api/core/model_runtime/entities/defaults.py index 776f6802e6..87fe4f681c 100644 --- a/api/core/model_runtime/entities/defaults.py +++ b/api/core/model_runtime/entities/defaults.py @@ -73,8 +73,8 @@ PARAMETER_RULE_TEMPLATE: dict[DefaultParameterName, dict] = { }, 'type': 'int', 'help': { - 'en_US': 'The maximum number of tokens to generate. Requests can use up to 2048 tokens shared between prompt and completion.', - 'zh_Hans': '要生成的标记的最大数量。请求可以使用最多2048个标记,这些标记在提示和完成之间共享。', + 'en_US': 'Specifies the upper limit on the length of generated results. If the generated results are truncated, you can increase this parameter.', + 'zh_Hans': '指定生成结果长度的上限。如果生成结果截断,可以调大该参数。', }, 'required': False, 'default': 64, From 52804ca6d1fcdb66c9f54f0fa6f8dfd3af1be294 Mon Sep 17 00:00:00 2001 From: legao <837937787@qq.com> Date: Mon, 25 Mar 2024 07:09:01 +0000 Subject: [PATCH 05/18] fix: adjust popup panel's z-index value (#2952) --- web/app/(commonLayout)/apps/AppCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/(commonLayout)/apps/AppCard.tsx b/web/app/(commonLayout)/apps/AppCard.tsx index bc6e84fab7..48e7d2a17a 100644 --- a/web/app/(commonLayout)/apps/AppCard.tsx +++ b/web/app/(commonLayout)/apps/AppCard.tsx @@ -167,7 +167,7 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { style.actionIconWrapper, ) } - className={'!w-[128px] h-fit !z-20'} + className={'!w-[128px] h-fit !z-0'} manualClose />} From d5214e4644230488a589b8653a5a43a85660e02d Mon Sep 17 00:00:00 2001 From: legao <837937787@qq.com> Date: Mon, 25 Mar 2024 07:13:50 +0000 Subject: [PATCH 06/18] reuse layout (#2956) --- web/app/(commonLayout)/datasets/NewDatasetCard.tsx | 10 ++++++---- web/app/components/header/HeaderWrapper.tsx | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/web/app/(commonLayout)/datasets/NewDatasetCard.tsx b/web/app/(commonLayout)/datasets/NewDatasetCard.tsx index 72f6b18dcc..079fd0e82b 100644 --- a/web/app/(commonLayout)/datasets/NewDatasetCard.tsx +++ b/web/app/(commonLayout)/datasets/NewDatasetCard.tsx @@ -1,16 +1,16 @@ 'use client' -import { forwardRef, useState } from 'react' +import { forwardRef } from 'react' import classNames from 'classnames' import { useTranslation } from 'react-i18next' +import Link from 'next/link' import style from '../list.module.css' const CreateAppCard = forwardRef((_, ref) => { const { t } = useTranslation() - const [showNewAppDialog, setShowNewAppDialog] = useState(false) return ( - +
@@ -21,8 +21,10 @@ const CreateAppCard = forwardRef((_, ref) => {
{t('dataset.createDatasetIntro')}
{/*
{t('app.createFromConfigFile')}
*/} -
+ ) }) +CreateAppCard.displayName = 'CreateAppCard' + export default CreateAppCard diff --git a/web/app/components/header/HeaderWrapper.tsx b/web/app/components/header/HeaderWrapper.tsx index 0e02195ac2..e0f6cfc111 100644 --- a/web/app/components/header/HeaderWrapper.tsx +++ b/web/app/components/header/HeaderWrapper.tsx @@ -11,7 +11,7 @@ const HeaderWrapper = ({ children, }: HeaderWrapperProps) => { const pathname = usePathname() - const isBordered = ['/apps', '/datasets'].includes(pathname) + const isBordered = ['/apps', '/datasets', '/datasets/create'].includes(pathname) return (
Date: Mon, 25 Mar 2024 22:20:40 +0800 Subject: [PATCH 07/18] When disabling the "Annotation Reply" button, the backend reports an error. #2904 (#2933) Co-authored-by: colvin --- api/tasks/annotation/disable_annotation_reply_task.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/api/tasks/annotation/disable_annotation_reply_task.py b/api/tasks/annotation/disable_annotation_reply_task.py index 7b88d7ac50..e5e03c9b51 100644 --- a/api/tasks/annotation/disable_annotation_reply_task.py +++ b/api/tasks/annotation/disable_annotation_reply_task.py @@ -9,7 +9,7 @@ from core.rag.datasource.vdb.vector_factory import Vector from extensions.ext_database import db from extensions.ext_redis import redis_client from models.dataset import Dataset -from models.model import App, AppAnnotationSetting +from models.model import App, AppAnnotationSetting, MessageAnnotation @shared_task(queue='dataset') @@ -25,7 +25,7 @@ def disable_annotation_reply_task(job_id: str, app_id: str, tenant_id: str): App.tenant_id == tenant_id, App.status == 'normal' ).first() - + annotations_count = db.session.query(MessageAnnotation).filter(MessageAnnotation.app_id == app_id).count() if not app: raise NotFound("App not found") @@ -49,8 +49,9 @@ def disable_annotation_reply_task(job_id: str, app_id: str, tenant_id: str): ) try: - vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id']) - vector.delete_by_metadata_field('app_id', app_id) + if annotations_count > 0: + vector = Vector(dataset, attributes=['doc_id', 'annotation_id', 'app_id']) + vector.delete_by_metadata_field('app_id', app_id) except Exception: logging.exception("Delete annotation index failed when annotation deleted.") redis_client.setex(disable_app_annotation_job_key, 600, 'completed') From 58e4702b140fd62cda08b6526d8dd6cc94efcf34 Mon Sep 17 00:00:00 2001 From: Ricky <5317425+rickythink@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:10:14 +0800 Subject: [PATCH 08/18] fix: white screen when editing annotaion in log panel (#2968) --- web/app/components/app/chat/index.tsx | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/web/app/components/app/chat/index.tsx b/web/app/components/app/chat/index.tsx index 0abdaab6d5..f8be6e6e80 100644 --- a/web/app/components/app/chat/index.tsx +++ b/web/app/components/app/chat/index.tsx @@ -212,16 +212,18 @@ const Chat: FC = ({ if (i === index) { return { ...item, - content: answer, annotation: { ...item.annotation, - logAnnotation: undefined, + logAnnotation: { + ...item.annotation?.logAnnotation, + content: answer, + }, } as any, } } return item })) - }, []) + }, [chatList]) const handleAnnotationAdded = useCallback((annotationId: string, authorName: string, query: string, answer: string, index: number) => { onChatListChange?.(chatList.map((item, i) => { if (i === index - 1) { @@ -251,7 +253,7 @@ const Chat: FC = ({ } return item })) - }, []) + }, [chatList]) const handleAnnotationRemoved = useCallback((index: number) => { onChatListChange?.(chatList.map((item, i) => { if (i === index) { @@ -261,12 +263,13 @@ const Chat: FC = ({ annotation: { ...(item.annotation || {}), id: '', + logAnnotation: undefined, // remove log } as Annotation, } } return item })) - }, []) + }, [chatList]) return (
From 1f98a4fff382e0356dfdd135fb7aeaf8c3c7fc2e Mon Sep 17 00:00:00 2001 From: Bowen Liang Date: Tue, 26 Mar 2024 10:11:43 +0800 Subject: [PATCH 09/18] improve: cache tool icons by setting max-age HTTP header and enable gzip compression SVG icons from backend (#2971) --- api/config.py | 4 +++- api/controllers/console/workspace/tool_providers.py | 5 +++-- api/extensions/ext_compress.py | 6 ++++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/api/config.py b/api/config.py index 61ccdfee7b..d602db5c2c 100644 --- a/api/config.py +++ b/api/config.py @@ -59,7 +59,8 @@ DEFAULTS = { 'CAN_REPLACE_LOGO': 'False', 'ETL_TYPE': 'dify', 'KEYWORD_STORE': 'jieba', - 'BATCH_UPLOAD_LIMIT': 20 + 'BATCH_UPLOAD_LIMIT': 20, + 'TOOL_ICON_CACHE_MAX_AGE': 3600, } @@ -298,6 +299,7 @@ class Config: self.BATCH_UPLOAD_LIMIT = get_env('BATCH_UPLOAD_LIMIT') self.API_COMPRESSION_ENABLED = get_bool_env('API_COMPRESSION_ENABLED') + self.TOOL_ICON_CACHE_MAX_AGE = get_env('TOOL_ICON_CACHE_MAX_AGE') class CloudEditionConfig(Config): diff --git a/api/controllers/console/workspace/tool_providers.py b/api/controllers/console/workspace/tool_providers.py index 931979c7f3..f9c2bc8d1c 100644 --- a/api/controllers/console/workspace/tool_providers.py +++ b/api/controllers/console/workspace/tool_providers.py @@ -1,6 +1,6 @@ import io -from flask import send_file +from flask import current_app, send_file from flask_login import current_user from flask_restful import Resource, reqparse from werkzeug.exceptions import Forbidden @@ -80,7 +80,8 @@ class ToolBuiltinProviderIconApi(Resource): @setup_required def get(self, provider): icon_bytes, minetype = ToolManageService.get_builtin_tool_provider_icon(provider) - return send_file(io.BytesIO(icon_bytes), mimetype=minetype) + icon_cache_max_age = int(current_app.config.get('TOOL_ICON_CACHE_MAX_AGE')) + return send_file(io.BytesIO(icon_bytes), mimetype=minetype, max_age=icon_cache_max_age) class ToolModelProviderIconApi(Resource): @setup_required diff --git a/api/extensions/ext_compress.py b/api/extensions/ext_compress.py index caa61675fb..4a349d37b4 100644 --- a/api/extensions/ext_compress.py +++ b/api/extensions/ext_compress.py @@ -5,6 +5,12 @@ def init_app(app: Flask): if app.config.get('API_COMPRESSION_ENABLED', False): from flask_compress import Compress + app.config['COMPRESS_MIMETYPES'] = [ + 'application/json', + 'image/svg+xml', + 'text/html', + ] + compress = Compress() compress.init_app(app) From 129a9850eb3af2b43152e4e694dc5d1223646b55 Mon Sep 17 00:00:00 2001 From: Bowen Liang Date: Tue, 26 Mar 2024 10:13:35 +0800 Subject: [PATCH 10/18] fix: correct response hint for generated image to avoid illusion of regernerated image link (#2962) --- api/core/features/assistant_base_runner.py | 2 +- api/core/tools/tool/tool.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/features/assistant_base_runner.py b/api/core/features/assistant_base_runner.py index 1d9541070f..b43d1cd40e 100644 --- a/api/core/features/assistant_base_runner.py +++ b/api/core/features/assistant_base_runner.py @@ -145,7 +145,7 @@ class BaseAssistantApplicationRunner(AppRunner): result += f"result link: {response.message}. please tell user to check it." elif response.type == ToolInvokeMessage.MessageType.IMAGE_LINK or \ response.type == ToolInvokeMessage.MessageType.IMAGE: - result += "image has been created and sent to user already, you should tell user to check it now." + result += "image has been created and sent to user already, you do not need to create it, just tell the user to check it now." else: result += f"tool response: {response.message}." diff --git a/api/core/tools/tool/tool.py b/api/core/tools/tool/tool.py index 351ae4362e..103fb931c5 100644 --- a/api/core/tools/tool/tool.py +++ b/api/core/tools/tool/tool.py @@ -232,7 +232,7 @@ class Tool(BaseModel, ABC): result += f"result link: {response.message}. please tell user to check it." elif response.type == ToolInvokeMessage.MessageType.IMAGE_LINK or \ response.type == ToolInvokeMessage.MessageType.IMAGE: - result += "image has been created and sent to user already, you should tell user to check it now." + result += "image has been created and sent to user already, you do not need to create it, just tell the user to check it now." elif response.type == ToolInvokeMessage.MessageType.BLOB: if len(response.message) > 114: result += str(response.message[:114]) + '...' From 7c8c233cf4603105e8b0cd961c3e60621a0cd184 Mon Sep 17 00:00:00 2001 From: Leo Q Date: Tue, 26 Mar 2024 10:18:26 +0800 Subject: [PATCH 11/18] Add S3_ADDRESS_STYLE configuration option (#2934) --- api/config.py | 2 ++ api/extensions/ext_storage.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/api/config.py b/api/config.py index d602db5c2c..04328b0007 100644 --- a/api/config.py +++ b/api/config.py @@ -22,6 +22,7 @@ DEFAULTS = { 'SERVICE_API_URL': 'https://api.dify.ai', 'APP_WEB_URL': 'https://udify.app', 'FILES_URL': '', + 'S3_ADDRESS_STYLE': 'auto', 'STORAGE_TYPE': 'local', 'STORAGE_LOCAL_PATH': 'storage', 'CHECK_UPDATE_URL': 'https://updates.dify.ai', @@ -181,6 +182,7 @@ class Config: self.S3_ACCESS_KEY = get_env('S3_ACCESS_KEY') self.S3_SECRET_KEY = get_env('S3_SECRET_KEY') self.S3_REGION = get_env('S3_REGION') + self.S3_ADDRESS_STYLE = get_env('S3_ADDRESS_STYLE') self.AZURE_BLOB_ACCOUNT_NAME = get_env('AZURE_BLOB_ACCOUNT_NAME') self.AZURE_BLOB_ACCOUNT_KEY = get_env('AZURE_BLOB_ACCOUNT_KEY') self.AZURE_BLOB_CONTAINER_NAME = get_env('AZURE_BLOB_CONTAINER_NAME') diff --git a/api/extensions/ext_storage.py b/api/extensions/ext_storage.py index 497ce5d2b7..3a8e314d92 100644 --- a/api/extensions/ext_storage.py +++ b/api/extensions/ext_storage.py @@ -7,6 +7,7 @@ from typing import Union import boto3 from azure.storage.blob import AccountSasPermissions, BlobServiceClient, ResourceTypes, generate_account_sas +from botocore.client import Config from botocore.exceptions import ClientError from flask import Flask @@ -27,7 +28,8 @@ class Storage: aws_secret_access_key=app.config.get('S3_SECRET_KEY'), aws_access_key_id=app.config.get('S3_ACCESS_KEY'), endpoint_url=app.config.get('S3_ENDPOINT'), - region_name=app.config.get('S3_REGION') + region_name=app.config.get('S3_REGION'), + config=Config(s3={'addressing_style': app.config.get('S3_ADDRESS_STYLE')}) ) elif self.storage_type == 'azure-blob': self.bucket_name = app.config.get('AZURE_BLOB_CONTAINER_NAME') From eeaa3c16431bc556a19a1f1177e3a7e4f51a4338 Mon Sep 17 00:00:00 2001 From: crazywoola <100913391+crazywoola@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:26:34 +0800 Subject: [PATCH 12/18] Fix/2969 add model provider ollama not work (#2973) --- .../model-provider-page/model-modal/Form.tsx | 6 +- .../model-provider-page/model-modal/index.tsx | 2 +- .../provider-card/index.tsx | 6 +- .../provider-icon/index.tsx | 2 +- web/i18n/pt-BR/common.ts | 352 +++++++++++++----- web/i18n/pt-BR/custom.ts | 36 +- web/i18n/pt-BR/tools.ts | 21 +- web/i18n/uk-UA/tools.ts | 9 +- web/i18n/zh-Hans/tools.ts | 8 + 9 files changed, 309 insertions(+), 133 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx index 69f50f374e..2d1eb4fb65 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/Form.tsx @@ -161,7 +161,7 @@ const Form: FC = ({ flex justify-center items-center mr-2 w-4 h-4 border border-gray-300 rounded-full ${value[variable] === option.value && 'border-[5px] border-primary-600'} `} /> -
{option.label[language]}
+
{option.label[language] || option.label.en_US}
)) } @@ -206,9 +206,9 @@ const Form: FC = ({ return option.show_on.every(showOnItem => value[showOnItem.variable] === showOnItem.value) return true - }).map(option => ({ value: option.value, name: option.label[language] }))} + }).map(option => ({ value: option.value, name: option.label[language] || option.label.en_US }))} onSelect={item => handleFormChange(variable, item.value as string)} - placeholder={placeholder?.[language]} + placeholder={placeholder?.[language] || placeholder?.en_US} /> {fieldMoreInfo?.(formSchema)} {validating && changeKey === variable && } diff --git a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx index d09369a8db..25d8af0ac1 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/model-modal/index.tsx @@ -219,7 +219,7 @@ const ModelModal: FC = ({ const renderTitlePrefix = () => { const prefix = configurateMethod === ConfigurateMethodEnum.customizableModel ? t('common.operation.add') : t('common.operation.setup') - return `${prefix} ${provider.label[language]}` + return `${prefix} ${provider.label[language] || provider.label.en_US}` } return ( diff --git a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx index 8918c69d9c..02cf5a8846 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-card/index.tsx @@ -116,11 +116,11 @@ const ProviderCard: FC = ({ return ( ) } diff --git a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx index 27e6e40490..c618b6f1a9 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-icon/index.tsx @@ -25,7 +25,7 @@ const ProviderIcon: FC = ({ return (
- {provider.label[language]} + {provider.label[language] || provider.label.en_US}
) diff --git a/web/i18n/pt-BR/common.ts b/web/i18n/pt-BR/common.ts index 0c5fef090c..c142b05fdd 100644 --- a/web/i18n/pt-BR/common.ts +++ b/web/i18n/pt-BR/common.ts @@ -1,39 +1,210 @@ const translation = { - you: '(Você)', + api: { + success: 'Sucesso', + actionSuccess: 'Ação bem-sucedida', + saved: 'Salvo', + create: 'Criado', + remove: 'Removido', + }, + operation: { + create: 'Criar', + confirm: 'Confirmar', + cancel: 'Cancelar', + clear: 'Limpar', + save: 'Salvar', + edit: 'Editar', + add: 'Adicionar', + added: 'Adicionado', + refresh: 'Reiniciar', + reset: 'Redefinir', + search: 'Buscar', + change: 'Alterar', + remove: 'Remover', + send: 'Enviar', + copy: 'Copiar', + lineBreak: 'Quebra de linha', + sure: 'Tenho certeza', + download: 'Baixar', + delete: 'Excluir', + settings: 'Configurações', + setup: 'Configuração', + getForFree: 'Obter gratuitamente', + reload: 'Recarregar', + ok: 'OK', + log: 'Log', + learnMore: 'Saiba Mais', + params: 'Parâmetros', + }, + placeholder: { + input: 'Por favor, insira', + select: 'Por favor, selecione', + }, + voice: { + language: { + zhHans: 'Chinês', + enUS: 'Inglês', + deDE: 'Alemão', + frFR: 'Francês', + esES: 'Espanhol', + itIT: 'Italiano', + thTH: 'Tailandês', + idID: 'Indonésio', + jaJP: 'Japonês', + koKR: 'Coreano', + ptBR: 'Português', + ruRU: 'Russo', + ukUA: 'Ucraniano', + }, + }, + unit: { + char: 'caracteres', + }, + actionMsg: { + noModification: 'Sem modificações no momento.', + modifiedSuccessfully: 'Modificado com sucesso', + modifiedUnsuccessfully: 'Modificado sem sucesso', + copySuccessfully: 'Copiado com sucesso', + paySucceeded: 'Pagamento realizado com sucesso', + payCancelled: 'Pagamento cancelado', + generatedSuccessfully: 'Gerado com sucesso', + generatedUnsuccessfully: 'Geração sem sucesso', + }, + model: { + params: { + temperature: 'Temperatura', + temperatureTip: + 'Controla a aleatoriedade: Diminuir resulta em conclusões menos aleatórias. À medida que a temperatura se aproxima de zero, o modelo se tornará determinístico e repetitivo.', + top_p: 'Top P', + top_pTip: + 'Controla a diversidade via amostragem de núcleo: 0.5 significa que metade de todas as opções ponderadas por probabilidade são consideradas.', + presence_penalty: 'Penalidade de presença', + presence_penaltyTip: + 'Quanto penalizar novos tokens com base em se eles aparecem no texto até agora.\nAumenta a probabilidade do modelo de falar sobre novos tópicos.', + frequency_penalty: 'Penalidade de frequência', + frequency_penaltyTip: + 'Quanto penalizar novos tokens com base em sua frequência existente no texto até agora.\nDiminui a probabilidade do modelo de repetir a mesma linha textualmente.', + max_tokens: 'Máximo de tokens', + max_tokensTip: + 'Usado para limitar o comprimento máximo da resposta, em tokens. \nValores maiores podem limitar o espaço restante para palavras de prompt, registros de bate-papo e Conhecimento. \nRecomenda-se defini-lo abaixo de dois terços\ngpt-4-1106-preview, gpt-4-vision-preview max token (entrada 128k saída 4k)', + maxTokenSettingTip: 'Sua configuração máxima de token é alta, limitando potencialmente o espaço para palavras de prompt, consultas e dados. Considere definir abaixo de 2/3.', + setToCurrentModelMaxTokenTip: 'O máximo de tokens é atualizado para 80% do máximo de token do modelo atual {{maxToken}}.', + stop_sequences: 'Sequências de parada', + stop_sequencesTip: 'Até quatro sequências onde a API irá parar de gerar mais tokens. O texto retornado não conterá a sequência de parada.', + stop_sequencesPlaceholder: 'Digite a sequência e pressione Tab', + }, + tone: { + Creative: 'Criativo', + Balanced: 'Equilibrado', + Precise: 'Preciso', + Custom: 'Personalizado', + }, + addMoreModel: 'Vá para configurações para adicionar mais modelos', + }, + menus: { + status: 'beta', + explore: 'Explorar', + apps: 'Estúdio', + plugins: 'Plugins', + pluginsTips: 'Integre plugins de terceiros ou crie plugins de IA compatíveis com o ChatGPT.', + datasets: 'Conhecimento', + datasetsTips: 'EM BREVE: Importe seus próprios dados de texto ou escreva dados em tempo real via Webhook para aprimoramento do contexto LLM.', + newApp: 'Novo App', + newDataset: 'Criar Conhecimento', + tools: 'Ferramentas', + }, + userProfile: { + settings: 'Configurações', + workspace: 'Espaço de trabalho', + createWorkspace: 'Criar Espaço de Trabalho', + helpCenter: 'Ajuda', + roadmapAndFeedback: 'Roadmap e Feedback', + community: 'Comunidade', + about: 'Sobre', + logout: 'Sair', + }, + settings: { + accountGroup: 'CONTA', + workplaceGroup: 'ESPAÇO DE TRABALHO', + account: 'Minha conta', + members: 'Membros', + billing: 'Faturamento', + integrations: 'Integrações', + language: 'Idioma', + provider: 'Fornecedor de modelo', + dataSource: 'Fonte de dados', + plugin: 'Plugins', + apiBasedExtension: 'Extensão baseada em API', + }, + account: { + avatar: 'Avatar', + name: 'Nome', + email: 'E-mail', + password: 'Senha', + passwordTip: 'Você pode definir uma senha permanente se não quiser usar códigos de login temporários', + setPassword: 'Definir uma senha', + resetPassword: 'Redefinir senha', + currentPassword: 'Senha atual', + newPassword: 'Nova senha', + confirmPassword: 'Confirmar senha', + notEqual: 'As duas senhas são diferentes.', + langGeniusAccount: 'Conta Dify', + langGeniusAccountTip: 'Sua conta Dify e dados de usuário associados.', + editName: 'Editar Nome', + showAppLength: 'Mostrar {{length}} apps', + }, + members: { + team: 'Equipe', + invite: 'Adicionar', + name: 'NOME', + lastActive: 'ÚLTIMA ATIVIDADE', + role: 'FUNÇÕES', + pending: 'Pendente...', + owner: 'Proprietário', + admin: 'Admin', + adminTip: 'Pode criar aplicativos e gerenciar configurações da equipe', + normal: 'Normal', + normalTip: 'Só pode usar aplicativos, não pode criar aplicativos', + inviteTeamMember: 'Adicionar membro da equipe', + inviteTeamMemberTip: 'Eles podem acessar os dados da sua equipe diretamente após fazer login.', + email: 'E-mail', + emailInvalid: 'Formato de e-mail inválido', + emailPlaceholder: 'Por favor, insira e-mails', + sendInvite: 'Enviar Convite', + invitedAsRole: 'Convidado como usuário {{role}}', + invitationSent: 'Convite enviado', + invitationSentTip: 'Convite enviado e eles podem fazer login no Dify para acessar os dados da sua equipe.', + invitationLink: 'Link do Convite', + failedinvitationEmails: 'Os seguintes usuários não foram convidados com sucesso', + ok: 'OK', + removeFromTeam: 'Remover da equipe', + removeFromTeamTip: 'Removerá o acesso da equipe', + setAdmin: 'Definir como administrador', + setMember: 'Definir como membro comum', + disinvite: 'Cancelar o convite', + deleteMember: 'Excluir Membro', + you: '(Você)', + }, integrations: { connected: 'Conectado', google: 'Google', - googleAccount: 'Entrar com conta do Google', + googleAccount: 'Faça login com a conta do Google', github: 'GitHub', - githubAccount: 'Entrar com conta do GitHub', + githubAccount: 'Faça login com a conta do GitHub', connect: 'Conectar', }, language: { displayLanguage: 'Idioma de exibição', timezone: 'Fuso horário', }, - voice: { - language: { - zhHans: 'chinês', - enUS: 'inglês', - deDE: 'alemão', - frFR: 'francês', - esES: 'espanhol', - itIT: 'italiano', - thTH: 'tailandês', - idID: 'indonésio', - ukUA: 'ucraniana', - }, - }, provider: { apiKey: 'Chave da API', enterYourKey: 'Insira sua chave da API aqui', - invalidKey: 'Chave da API inválida', + invalidKey: 'Chave da API OpenAI inválida', validatedError: 'Falha na validação: ', validating: 'Validando chave...', saveFailed: 'Falha ao salvar a chave da API', - apiKeyExceedBill: 'Esta chave da API não possui cota disponível, por favor leia', - addKey: 'Adicionar chave', + apiKeyExceedBill: 'Esta CHAVE DE API não tem quota disponível, por favor, leia', + addKey: 'Adicionar Chave', comingSoon: 'Em breve', editKey: 'Editar', invalidApiKey: 'Chave da API inválida', @@ -42,49 +213,33 @@ const translation = { apiBasePlaceholder: 'A URL base da API do seu ponto de extremidade Azure OpenAI.', apiKey: 'Chave da API', apiKeyPlaceholder: 'Insira sua chave da API aqui', - helpTip: 'Aprenda sobre o Serviço Azure OpenAI', + helpTip: 'Saiba mais sobre o Serviço Azure OpenAI', }, openaiHosted: { openaiHosted: 'OpenAI Hospedado', onTrial: 'EM TESTE', exhausted: 'COTA ESGOTADA', - desc: 'O serviço de hospedagem OpenAI fornecido pela Dify permite que você use modelos como o GPT-3.5. Antes que sua cota de teste seja esgotada, você precisa configurar outros provedores de modelo.', - callTimes: 'Número de chamadas', - usedUp: 'Cota de teste esgotada. Adicione seu próprio provedor de modelo.', - useYourModel: 'Atualmente usando seu próprio provedor de modelo.', + desc: 'O serviço de hospedagem OpenAI fornecido pela Dify permite que você use modelos como GPT-3.5. Antes que sua cota de teste seja esgotada, você precisa configurar outros fornecedores de modelos.', + callTimes: 'Chamadas', + usedUp: 'Cota de teste esgotada. Adicione seu próprio Fornecedor de Modelo.', + useYourModel: 'Atualmente usando seu próprio Fornecedor de Modelo.', close: 'Fechar', }, - voice: { - language: { - zhHans: 'chinês', - enUS: 'inglês', - deDE: 'alemão', - frFR: 'francês', - esES: 'espanhol', - itIT: 'italiano', - thTH: 'tailandês', - idID: 'indonésio', - jaJP: 'japonês', - koKR: 'coreano', - ptBR: 'português', - ruRU: 'russo', - }, - }, anthropicHosted: { anthropicHosted: 'Anthropic Claude', onTrial: 'EM TESTE', exhausted: 'COTA ESGOTADA', desc: 'Modelo poderoso, que se destaca em uma ampla gama de tarefas, desde diálogos sofisticados e geração de conteúdo criativo até instruções detalhadas.', - callTimes: 'Número de chamadas', - usedUp: 'Cota de teste esgotada. Adicione seu próprio provedor de modelo.', - useYourModel: 'Atualmente usando seu próprio provedor de modelo.', + callTimes: 'Chamadas', + usedUp: 'Cota de teste esgotada. Adicione seu próprio Fornecedor de Modelo.', + useYourModel: 'Atualmente usando seu próprio Fornecedor de Modelo.', close: 'Fechar', }, anthropic: { - using: 'A capacidade de incorporação está usando', - enableTip: 'Para habilitar o modelo Anthropic, você precisa se vincular ao OpenAI ou ao Azure OpenAI Service primeiro.', + using: 'A capacidade de incorporação está sendo utilizada', + enableTip: 'Para habilitar o modelo da Anthropic, você precisa vincular ao OpenAI ou ao Azure OpenAI Service primeiro.', notEnabled: 'Não habilitado', - keyFrom: 'Obtenha sua chave da API do Anthropic', + keyFrom: 'Obtenha sua chave da API da Anthropic', }, encrypted: { front: 'Sua CHAVE DA API será criptografada e armazenada usando', @@ -92,29 +247,33 @@ const translation = { }, }, modelProvider: { - notConfigured: 'O modelo do sistema ainda não foi totalmente configurado e algumas funções podem não estar disponíveis.', - systemModelSettings: 'Configurações do modelo do sistema', + notConfigured: 'O modelo do sistema ainda não foi totalmente configurado e algumas funções podem estar indisponíveis.', + systemModelSettings: 'Configurações do Modelo do Sistema', systemModelSettingsLink: 'Por que é necessário configurar um modelo do sistema?', selectModel: 'Selecione seu modelo', setupModelFirst: 'Por favor, configure seu modelo primeiro', systemReasoningModel: { - key: 'Modelo de raciocínio do sistema', - tip: 'Defina o modelo de inferência padrão a ser usado para criar aplicativos, bem como recursos como geração de nome de diálogo e sugestão de próxima pergunta também usarão o modelo de inferência padrão.', + key: 'Modelo de Raciocínio do Sistema', + tip: 'Defina o modelo de inferência padrão a ser usado para criar aplicativos, bem como recursos como geração de nomes de diálogo e sugestão de próxima pergunta também usarão o modelo de inferência padrão.', }, embeddingModel: { - key: 'Modelo de incorporação', - tip: 'Defina o modelo padrão para o processamento de incorporação de documentos do Conhecimento, tanto a recuperação quanto a importação do Conhecimento usam este modelo de Incorporação para o processamento de vetorização. A troca causará inconsistência na dimensão do vetor entre o Conhecimento importado e a pergunta, resultando em falha na recuperação. Para evitar falhas na recuperação, não altere este modelo indiscriminadamente.', - required: 'O modelo de incorporação é obrigatório', + key: 'Modelo de Incorporação', + tip: 'Defina o modelo padrão para o processamento de incorporação de documentos do Conhecimento, tanto a recuperação quanto a importação do Conhecimento usam este modelo de Incorporação para processamento de vetorização. Alterar causará inconsistência na dimensão do vetor entre o Conhecimento importado e a pergunta, resultando em falha na recuperação. Para evitar falhas na recuperação, não altere este modelo indiscriminadamente.', + required: 'O modelo de Incorporação é obrigatório', }, speechToTextModel: { - key: 'Modelo de fala para texto', - tip: 'Defina o modelo padrão para entrada de fala para texto em conversa.', + key: 'Modelo de Fala para Texto', + tip: 'Defina o modelo padrão para entrada de fala para texto na conversa.', + }, + ttsModel: { + key: 'Modelo de Texto para Fala', + tip: 'Defina o modelo padrão para entrada de texto para fala na conversa.', }, rerankModel: { - key: 'Modelo de reclassificação', - tip: 'O modelo de reclassificação reordenará a lista de documentos candidatos com base na correspondência semântica com a consulta do usuário, melhorando os resultados da classificação semântica', + key: 'Modelo de Reordenação', + tip: 'O modelo de reordenaenação reorganizará a lista de documentos candidatos com base na correspondência semântica com a consulta do usuário, melhorando os resultados da classificação semântica', }, - quota: 'Cota', + quota: 'Quota', searchModel: 'Modelo de pesquisa', noModelFound: 'Nenhum modelo encontrado para {{model}}', models: 'Modelos', @@ -122,24 +281,24 @@ const translation = { selector: { tip: 'Este modelo foi removido. Adicione um modelo ou selecione outro modelo.', emptyTip: 'Nenhum modelo disponível', - emptySetting: 'Por favor, vá para as configurações para configurar', - rerankTip: 'Por favor, configure o modelo de reclassificação', + emptySetting: 'Por favor, vá para configurações para configurar', + rerankTip: 'Por favor, configure o modelo de reordenação', }, card: { - quota: 'COTA', + quota: 'QUOTA', onTrial: 'Em Teste', paid: 'Pago', - quotaExhausted: 'Cota esgotada', - callTimes: 'Número de chamadas', + quotaExhausted: 'Quota esgotada', + callTimes: 'Chamadas', tokens: 'Tokens', - buyQuota: 'Comprar Cota', + buyQuota: 'Comprar Quota', priorityUse: 'Uso prioritário', removeKey: 'Remover Chave da API', - tip: 'A cota paga terá prioridade. A cota de teste será usada após a cota paga ser esgotada.', + tip: 'A prioridade será dada à quota paga. A quota de teste será usada após a quota paga ser esgotada.', }, item: { deleteDesc: '{{modelName}} está sendo usado como modelos de raciocínio do sistema. Algumas funções não estarão disponíveis após a remoção. Por favor, confirme.', - freeQuota: 'COTA GRATUITA', + freeQuota: 'QUOTA GRATUITA', }, addApiKey: 'Adicionar sua chave da API', invalidApiKey: 'Chave da API inválida', @@ -150,24 +309,25 @@ const translation = { freeQuota: { howToEarn: 'Como ganhar', }, - addMoreModelProvider: 'ADICIONAR MAIS PROVEDOR DE MODELO', + addMoreModelProvider: 'ADICIONAR MAIS FORNECEDOR DE MODELO', addModel: 'Adicionar Modelo', modelsNum: '{{num}} Modelos', showModels: 'Mostrar Modelos', showModelsNum: 'Mostrar {{num}} Modelos', collapse: 'Recolher', - config: 'Configurar', + config: 'Configuração', modelAndParameters: 'Modelo e Parâmetros', model: 'Modelo', featureSupported: '{{feature}} suportado', - callTimes: 'Número de chamadas', - buyQuota: 'Comprar Cota', - getFreeTokens: 'Obter Tokens gratuitos', + callTimes: 'Chamadas', + credits: 'Créditos de Mensagem', + buyQuota: 'Comprar Quota', + getFreeTokens: 'Obter Tokens Gratuitos', priorityUsing: 'Uso prioritário', - deprecated: 'Descontinuado', + deprecated: 'Obsoleto', confirmDelete: 'confirmar exclusão?', quotaTip: 'Tokens gratuitos disponíveis restantes', - loadPresets: 'Carregar presentes', + loadPresets: 'Carregar Predefinições', parameters: 'PARÂMETROS', }, dataSource: { @@ -197,11 +357,11 @@ const translation = { serpapi: { apiKey: 'Chave da API', apiKeyPlaceholder: 'Insira sua chave da API', - keyFrom: 'Obtenha sua chave SerpAPI na página da conta SerpAPI', + keyFrom: 'Obtenha sua chave da SerpAPI na página da conta da SerpAPI', }, }, apiBasedExtension: { - title: 'As extensões de API fornecem gerenciamento centralizado de API, simplificando a configuração para uso fácil em aplicativos da Dify.', + title: 'As extensões de API fornecem gerenciamento centralizado de API, simplificando a configuração para uso fácil em todos os aplicativos da Dify.', link: 'Saiba como desenvolver sua própria Extensão de API.', linkUrl: 'https://docs.dify.ai/features/extension/api_based_extension', add: 'Adicionar Extensão de API', @@ -230,13 +390,13 @@ const translation = { type: 'Tipo', }, about: { - changeLog: 'Registro de alterações', + changeLog: 'Registro de Alterações', updateNow: 'Atualizar agora', - nowAvailable: 'Dify {{version}} está disponível agora.', - latestAvailable: 'Dify {{version}} é a versão mais recente disponível.', + nowAvailable: 'Dify {{version}} já está disponível.', + latestAvailable: 'Dify {{version}} é a última versão disponível.', }, appMenus: { - overview: 'Visão geral', + overview: 'Visão Geral', promptEng: 'Orquestrar', apiAccess: 'Acesso à API', logAndAnn: 'Logs e Anúncios', @@ -247,7 +407,7 @@ const translation = { }, appModes: { completionApp: 'Gerador de Texto', - chatApp: 'Aplicativo de Chat', + chatApp: 'Aplicativo de Bate-papo', }, datasetMenus: { documents: 'Documentos', @@ -255,7 +415,7 @@ const translation = { settings: 'Configurações', emptyTip: 'O Conhecimento não foi associado, por favor, vá para o aplicativo ou plug-in para completar a associação.', viewDoc: 'Ver documentação', - relatedApp: 'aplicativos vinculados', + relatedApp: 'aplicativos relacionados', }, voiceInput: { speaking: 'Fale agora...', @@ -267,10 +427,10 @@ const translation = { 'gpt-3.5-turbo-16k': 'GPT-3.5-Turbo-16K', 'gpt-4': 'GPT-4', 'gpt-4-32k': 'GPT-4-32K', - 'text-davinci-003': 'Text-Davinci-003', - 'text-embedding-ada-002': 'Text-Embedding-Ada-002', - 'whisper-1': 'Whisper-1', - 'claude-instant-1': 'Claude-Instant', + 'text-davinci-003': 'Texto-Davinci-003', + 'text-embedding-ada-002': 'Texto-Embutimento-Ada-002', + 'whisper-1': 'Sussurro-1', + 'claude-instant-1': 'Claude-Instantâneo', 'claude-2': 'Claude-2', }, chat: { @@ -280,29 +440,29 @@ const translation = { conversationNameCanNotEmpty: 'Nome da conversa obrigatório', citation: { title: 'CITAÇÕES', - linkToDataset: 'Link para o Conhecimento', + linkToDataset: 'Link para Conhecimento', characters: 'Personagens:', hitCount: 'Contagem de recuperação:', - vectorHash: 'Hash do vetor:', + vectorHash: 'Hash de vetor:', hitScore: 'Pontuação de recuperação:', }, }, promptEditor: { - placeholder: 'Escreva sua palavra de estímulo aqui, digite \'{\' para inserir uma variável, digite \'/\' para inserir um bloco de conteúdo de estímulo', + placeholder: 'Escreva sua palavra de incentivo aqui, digite \'{\' para inserir uma variável, digite \'/\' para inserir um bloco de conteúdo de incentivo', context: { item: { title: 'Contexto', desc: 'Inserir modelo de contexto', }, modal: { - title: '{{num}} Conhecimento em Contexto', + title: '{{num}} Conhecimentos no Contexto', add: 'Adicionar Contexto', - footer: 'Você pode gerenciar os contextos na seção de Contexto abaixo.', + footer: 'Você pode gerenciar contextos na seção Contexto abaixo.', }, }, history: { item: { - title: 'Histórico da Conversa', + title: 'Histórico de Conversas', desc: 'Inserir modelo de mensagem histórica', }, modal: { @@ -328,17 +488,17 @@ const translation = { desc: 'Inserir modelo de consulta do usuário', }, }, - existed: 'Já existe no estímulo', + existed: 'Já existe no incentivo', }, imageUploader: { - uploadFromComputer: 'Enviar do computador', - uploadFromComputerReadError: 'Falha na leitura da imagem, por favor, tente novamente.', - uploadFromComputerUploadError: 'Falha no envio da imagem, por favor, envie novamente.', + uploadFromComputer: 'Enviar do Computador', + uploadFromComputerReadError: 'Falha ao ler a imagem, por favor, tente novamente.', + uploadFromComputerUploadError: 'Falha ao enviar a imagem, por favor, envie novamente.', uploadFromComputerLimit: 'As imagens enviadas não podem exceder {{size}} MB', pasteImageLink: 'Colar link da imagem', pasteImageLinkInputPlaceholder: 'Cole o link da imagem aqui', pasteImageLinkInvalid: 'Link da imagem inválido', - imageUpload: 'Envio de Imagem', + imageUpload: 'Enviar Imagem', }, } diff --git a/web/i18n/pt-BR/custom.ts b/web/i18n/pt-BR/custom.ts index 2d931a8da2..940316e7bb 100644 --- a/web/i18n/pt-BR/custom.ts +++ b/web/i18n/pt-BR/custom.ts @@ -1,29 +1,29 @@ const translation = { - custom: 'Customization', + custom: 'Personalização', upgradeTip: { - prefix: 'Upgrade your plan to', - suffix: 'customize your brand.', + prefix: 'Atualize seu plano para', + suffix: 'personalizar sua marca.', }, webapp: { - title: 'Customize WebApp brand', - removeBrand: 'Remove Powered by Dify', - changeLogo: 'Change Powered by Brand Image', - changeLogoTip: 'SVG or PNG format with a minimum size of 40x40px', + title: 'Personalizar marca do WebApp', + removeBrand: 'Remover Powered by Dify', + changeLogo: 'Alterar Imagem da Marca Powered by', + changeLogoTip: 'Formato SVG ou PNG com tamanho mínimo de 40x40px', }, app: { - title: 'Customize app header brand', - changeLogoTip: 'SVG or PNG format with a minimum size of 80x80px', + title: 'Personalizar cabeçalho do aplicativo', + changeLogoTip: 'Formato SVG ou PNG com tamanho mínimo de 80x80px', }, - upload: 'Upload', - uploading: 'Uploading', - uploadedFail: 'Image upload failed, please re-upload.', - change: 'Change', - apply: 'Apply', - restore: 'Restore Defaults', + upload: 'Enviar', + uploading: 'Enviando', + uploadedFail: 'Falha no envio da imagem, por favor, envie novamente.', + change: 'Alterar', + apply: 'Aplicar', + restore: 'Restaurar Padrões', customize: { - contactUs: ' contact us ', - prefix: 'To customize the brand logo within the app, please', - suffix: 'to upgrade to the Enterprise edition.', + contactUs: ' entre em contato conosco ', + prefix: 'Para personalizar o logotipo da marca dentro do aplicativo, por favor', + suffix: 'para fazer upgrade para a edição Enterprise.', }, } diff --git a/web/i18n/pt-BR/tools.ts b/web/i18n/pt-BR/tools.ts index 3434bd15ee..7757f7e00f 100644 --- a/web/i18n/pt-BR/tools.ts +++ b/web/i18n/pt-BR/tools.ts @@ -4,7 +4,7 @@ const translation = { type: { all: 'Todas', builtIn: 'Integradas', - custom: 'Personal...', + custom: 'Personalizadas', }, contribute: { line1: 'Estou interessado em ', @@ -13,7 +13,7 @@ const translation = { }, author: 'Por', auth: { - unauthorized: 'Não autorizado', + unauthorized: 'Para Autorizar', authorized: 'Autorizado', setup: 'Configurar autorização para usar', setupModalTitle: 'Configurar Autorização', @@ -35,8 +35,8 @@ const translation = { urlError: 'Digite uma URL válida', examples: 'Exemplos', exampleOptions: { - json: 'Clima (JSON)', - yaml: 'Pet Store (YAML)', + json: 'Clima(JSON)', + yaml: 'Pet Store(YAML)', blankTemplate: 'Modelo em Branco', }, availableTools: { @@ -51,18 +51,22 @@ const translation = { authMethod: { title: 'Método de Autorização', type: 'Tipo de Autorização', + keyTooltip: 'Chave do Cabeçalho HTTP, você pode deixar como "Authorization" se não tiver ideia do que é ou definir um valor personalizado', types: { none: 'Nenhum', api_key: 'Chave de API', + apiKeyPlaceholder: 'Nome do cabeçalho HTTP para a Chave de API', + apiValuePlaceholder: 'Digite a Chave de API', }, key: 'Chave', value: 'Valor', }, authHeaderPrefix: { + title: 'Tipo de Autenticação', types: { - basic: 'Basic', + basic: 'Básica', bearer: 'Bearer', - custom: 'Custom', + custom: 'Personalizada', }, }, privacyPolicy: 'Política de Privacidade', @@ -94,7 +98,7 @@ const translation = { }, noCustomTool: { title: 'Nenhuma ferramenta personalizada!', - content: 'Você não possui ferramentas personalizadas. ', + content: 'Adicione e gerencie suas ferramentas personalizadas aqui para construir aplicativos de IA.', createTool: 'Criar Ferramenta', }, noSearchRes: { @@ -104,7 +108,8 @@ const translation = { }, builtInPromptTitle: 'Prompt', toolRemoved: 'Ferramenta removida', - howToGet: 'Como conseguir', + notAuthorized: 'Ferramenta não autorizada', + howToGet: 'Como obter', } export default translation diff --git a/web/i18n/uk-UA/tools.ts b/web/i18n/uk-UA/tools.ts index 307149c386..da07116761 100644 --- a/web/i18n/uk-UA/tools.ts +++ b/web/i18n/uk-UA/tools.ts @@ -49,11 +49,14 @@ const translation = { test: 'Перевірка', }, authMethod: { - title: 'Спосіб авторизації', + title: 'Метод авторизації', type: 'Тип авторизації', + keyTooltip: 'Ключ HTTP-заголовка. Якщо ви не знаєте, залиште його як "Authorization" або встановіть власне значення', types: { - none: 'Немає', - api_key: 'API ключі', + none: 'Відсутня', + api_key: 'API-ключ', + apiKeyPlaceholder: 'Назва HTTP-заголовка для API-ключа', + apiValuePlaceholder: 'Введіть API-ключ', }, key: 'Ключ', value: 'Значення', diff --git a/web/i18n/zh-Hans/tools.ts b/web/i18n/zh-Hans/tools.ts index c709d62547..0396541d69 100644 --- a/web/i18n/zh-Hans/tools.ts +++ b/web/i18n/zh-Hans/tools.ts @@ -72,6 +72,14 @@ const translation = { privacyPolicy: '隐私协议', privacyPolicyPlaceholder: '请输入隐私协议', }, + test: { + title: '测试', + parametersValue: '参数和值', + parameters: '参数', + value: '值', + testResult: '测试结果', + testResultPlaceholder: '测试结果将显示在这里', + }, thought: { using: '正在使用', used: '已使用', From 08a5afcf9fd2055f8206501a90bea5e5d363e0b1 Mon Sep 17 00:00:00 2001 From: Kenny Date: Tue, 26 Mar 2024 10:37:43 +0800 Subject: [PATCH 13/18] feat: update nginx and docker-compose files to support HTTPS. (#2940) Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> --- docker/docker-compose.yaml | 2 ++ docker/nginx/conf.d/default.conf | 11 ++++++++++- docker/nginx/ssl/.gitkeep | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 docker/nginx/ssl/.gitkeep diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d5e90f8347..42a3e7055b 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -326,8 +326,10 @@ services: - ./nginx/nginx.conf:/etc/nginx/nginx.conf - ./nginx/proxy.conf:/etc/nginx/proxy.conf - ./nginx/conf.d:/etc/nginx/conf.d + #- ./nginx/ssl:/etc/ssl depends_on: - api - web ports: - "80:80" + #- "443:443" diff --git a/docker/nginx/conf.d/default.conf b/docker/nginx/conf.d/default.conf index 879ce63164..d6ee302b78 100644 --- a/docker/nginx/conf.d/default.conf +++ b/docker/nginx/conf.d/default.conf @@ -26,4 +26,13 @@ server { proxy_pass http://web:3000; include proxy.conf; } -} \ No newline at end of file + + # If you want to support HTTPS, please uncomment the code snippet below + #listen 443 ssl; + #ssl_certificate ./../ssl/your_cert_file.cer; + #ssl_certificate_key ./../ssl/your_cert_key.key; + #ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; + #ssl_prefer_server_ciphers on; + #ssl_session_cache shared:SSL:10m; + #ssl_session_timeout 10m; +} diff --git a/docker/nginx/ssl/.gitkeep b/docker/nginx/ssl/.gitkeep new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/docker/nginx/ssl/.gitkeep @@ -0,0 +1 @@ + From a676d4387c11a9dc2aceb00a271abef51fc5cdb1 Mon Sep 17 00:00:00 2001 From: Weishan-0 <29089406+Weishan-0@users.noreply.github.com> Date: Tue, 26 Mar 2024 10:43:20 +0800 Subject: [PATCH 14/18] fix: Correct image parameter passing in GLM-4v model API calls (#2948) --- .../model_providers/zhipuai/llm/llm.py | 88 +++++++++++-------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py b/api/core/model_runtime/model_providers/zhipuai/llm/llm.py index 27277164c9..ee09b8cb74 100644 --- a/api/core/model_runtime/model_providers/zhipuai/llm/llm.py +++ b/api/core/model_runtime/model_providers/zhipuai/llm/llm.py @@ -5,6 +5,7 @@ from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, PromptMessage, + PromptMessageContent, PromptMessageContentType, PromptMessageRole, PromptMessageTool, @@ -31,6 +32,7 @@ And you should always end the block with a "```" to indicate the end of the JSON ```JSON""" + class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): def _invoke(self, model: str, credentials: dict, @@ -159,7 +161,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): if len(prompt_messages) == 0: raise ValueError('At least one message is required') - + if prompt_messages[0].role == PromptMessageRole.SYSTEM: if not prompt_messages[0].content: prompt_messages = prompt_messages[1:] @@ -185,7 +187,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): continue if new_prompt_messages and new_prompt_messages[-1].role == PromptMessageRole.USER and \ - copy_prompt_message.role == PromptMessageRole.USER: + copy_prompt_message.role == PromptMessageRole.USER: new_prompt_messages[-1].content += "\n\n" + copy_prompt_message.content else: if copy_prompt_message.role == PromptMessageRole.USER: @@ -205,31 +207,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): new_prompt_messages.append(copy_prompt_message) if model == 'glm-4v': - params = { - 'model': model, - 'messages': [{ - 'role': prompt_message.role.value, - 'content': - [ - { - 'type': 'text', - 'text': prompt_message.content - } - ] if isinstance(prompt_message.content, str) else - [ - { - 'type': 'image', - 'image_url': { - 'url': content.data - } - } if content.type == PromptMessageContentType.IMAGE else { - 'type': 'text', - 'text': content.data - } for content in prompt_message.content - ], - } for prompt_message in new_prompt_messages], - **model_parameters - } + params = self._construct_glm_4v_parameter(model, new_prompt_messages, model_parameters) else: params = { 'model': model, @@ -277,8 +255,8 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): for prompt_message in new_prompt_messages: # merge system message to user message if prompt_message.role == PromptMessageRole.SYSTEM or \ - prompt_message.role == PromptMessageRole.TOOL or \ - prompt_message.role == PromptMessageRole.USER: + prompt_message.role == PromptMessageRole.TOOL or \ + prompt_message.role == PromptMessageRole.USER: if len(params['messages']) > 0 and params['messages'][-1]['role'] == 'user': params['messages'][-1]['content'] += "\n\n" + prompt_message.content else: @@ -306,8 +284,44 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): response = client.chat.completions.create(**params, **extra_model_kwargs) return self._handle_generate_response(model, credentials_kwargs, tools, response, prompt_messages) - - def _handle_generate_response(self, model: str, + + def _construct_glm_4v_parameter(self, model: str, prompt_messages: list[PromptMessage], + model_parameters: dict): + messages = [ + { + 'role': message.role.value, + 'content': self._construct_glm_4v_messages(message.content) + } + for message in prompt_messages + ] + + params = { + 'model': model, + 'messages': messages, + **model_parameters + } + + return params + + def _construct_glm_4v_messages(self, prompt_message: Union[str | list[PromptMessageContent]]) -> list[dict]: + if isinstance(prompt_message, str): + return [{'type': 'text', 'text': prompt_message}] + + return [ + {'type': 'image_url', 'image_url': {'url': self._remove_image_header(item.data)}} + if item.type == PromptMessageContentType.IMAGE else + {'type': 'text', 'text': item.data} + + for item in prompt_message + ] + + def _remove_image_header(self, image: str) -> str: + if image.startswith('data:image'): + return image.split(',')[1] + + return image + + def _handle_generate_response(self, model: str, credentials: dict, tools: Optional[list[PromptMessageTool]], response: Completion, @@ -338,7 +352,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): ) text += choice.message.content or '' - + prompt_usage = response.usage.prompt_tokens completion_usage = response.usage.completion_tokens @@ -358,7 +372,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): return result - def _handle_generate_stream_response(self, model: str, + def _handle_generate_stream_response(self, model: str, credentials: dict, tools: Optional[list[PromptMessageTool]], responses: Generator[ChatCompletionChunk, None, None], @@ -380,7 +394,7 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): if delta.finish_reason is None and (delta.delta.content is None or delta.delta.content == ''): continue - + assistant_tool_calls: list[AssistantPromptMessage.ToolCall] = [] for tool_call in delta.delta.tool_calls or []: if tool_call.type == 'function': @@ -454,8 +468,8 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): return message_text - - def _convert_messages_to_prompt(self, messages: list[PromptMessage], tools: Optional[list[PromptMessageTool]] = None) -> str: + def _convert_messages_to_prompt(self, messages: list[PromptMessage], + tools: Optional[list[PromptMessageTool]] = None) -> str: """ :param messages: List of PromptMessage to combine. :return: Combined string with necessary human_prompt and ai_prompt tags. @@ -473,4 +487,4 @@ class ZhipuAILargeLanguageModel(_CommonZhipuaiAI, LargeLanguageModel): text += f"\n{tool.json()}" # trim off the trailing ' ' that might come from the "Assistant: " - return text.rstrip() \ No newline at end of file + return text.rstrip() From 8e052615880d18542d25e9686aa762e930d42174 Mon Sep 17 00:00:00 2001 From: Yulong Wang Date: Tue, 26 Mar 2024 10:53:39 +0800 Subject: [PATCH 15/18] Fix handling of missing required parameters in ApiTool (#2965) --- api/core/tools/tool/api_tool.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/core/tools/tool/api_tool.py b/api/core/tools/tool/api_tool.py index 54e2f41019..ab46dc61da 100644 --- a/api/core/tools/tool/api_tool.py +++ b/api/core/tools/tool/api_tool.py @@ -148,7 +148,7 @@ class ApiTool(Tool): value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] - elif parameter['required']: + elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') @@ -158,7 +158,7 @@ class ApiTool(Tool): value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] - elif parameter['required']: + elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') @@ -168,7 +168,7 @@ class ApiTool(Tool): value = '' if parameter['name'] in parameters: value = parameters[parameter['name']] - elif parameter['required']: + elif parameter.get('required', False): raise ToolParameterValidationError(f"Missing required parameter {parameter['name']}") else: value = (parameter.get('schema', {}) or {}).get('default', '') From d70bd4aaa44071ec9b4ad9c83869c29b55c54e3e Mon Sep 17 00:00:00 2001 From: listeng Date: Tue, 26 Mar 2024 11:05:10 +0800 Subject: [PATCH 16/18] fix tool_inputs parse error in message that in CoT(ReAct) agent mode (#2949) --- api/core/features/assistant_base_runner.py | 8 ++++++-- api/core/features/assistant_cot_runner.py | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/api/core/features/assistant_base_runner.py b/api/core/features/assistant_base_runner.py index b43d1cd40e..5feee64db1 100644 --- a/api/core/features/assistant_base_runner.py +++ b/api/core/features/assistant_base_runner.py @@ -566,7 +566,11 @@ class BaseAssistantApplicationRunner(AppRunner): tools = tools.split(';') tool_calls: list[AssistantPromptMessage.ToolCall] = [] tool_call_response: list[ToolPromptMessage] = [] - tool_inputs = json.loads(agent_thought.tool_input) + try: + tool_inputs = json.loads(agent_thought.tool_input) + except Exception as e: + logging.warning("tool execution error: {}, tool_input: {}.".format(str(e), agent_thought.tool_input)) + tool_inputs = { agent_thought.tool: agent_thought.tool_input } for tool in tools: # generate a uuid for tool call tool_call_id = str(uuid.uuid4()) @@ -599,4 +603,4 @@ class BaseAssistantApplicationRunner(AppRunner): db.session.close() - return result \ No newline at end of file + return result diff --git a/api/core/features/assistant_cot_runner.py b/api/core/features/assistant_cot_runner.py index 3762ddcf62..6d43d846e4 100644 --- a/api/core/features/assistant_cot_runner.py +++ b/api/core/features/assistant_cot_runner.py @@ -182,7 +182,7 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner): delta=LLMResultChunkDelta( index=0, message=AssistantPromptMessage( - content=json.dumps(chunk) + content=json.dumps(chunk, ensure_ascii=False) # if ensure_ascii=True, the text in webui maybe garbled text ), usage=None ) @@ -667,4 +667,4 @@ class AssistantCotApplicationRunner(BaseAssistantApplicationRunner): try: return json.dumps(tools, ensure_ascii=False) except json.JSONDecodeError: - return json.dumps(tools) \ No newline at end of file + return json.dumps(tools) From afd77c4745b6fce1c94970b0f38e866fcf52e375 Mon Sep 17 00:00:00 2001 From: Ricky <5317425+rickythink@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:05:29 +0800 Subject: [PATCH 17/18] fix: the batch annotaion btn should also be loading when progress status is waiting (#2974) --- .../app/annotation/batch-add-annotation-modal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx index 49934b6f98..aae54bf497 100644 --- a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx +++ b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx @@ -113,7 +113,7 @@ const BatchModal: FC = ({ type="primary" onClick={handleSend} disabled={isAnnotationFull || !currentCSV} - loading={importStatus === ProcessStatus.PROCESSING} + loading={importStatus === ProcessStatus.PROCESSING || importStatus === ProcessStatus.WAITING} > {t('appAnnotation.batchModal.run')} From 40dbf307848f6bf150393ca6a673dc258d97630d Mon Sep 17 00:00:00 2001 From: Ricky <5317425+rickythink@users.noreply.github.com> Date: Tue, 26 Mar 2024 11:34:40 +0800 Subject: [PATCH 18/18] feat: support new reranker [jina-colbert-v1-en] (#2975) --- .../model_providers/jina/rerank/jina-colbert-v1-en.yaml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml diff --git a/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml b/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml new file mode 100644 index 0000000000..320370f242 --- /dev/null +++ b/api/core/model_runtime/model_providers/jina/rerank/jina-colbert-v1-en.yaml @@ -0,0 +1,4 @@ +model: jina-colbert-v1-en +model_type: rerank +model_properties: + context_size: 8192