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 diff --git a/api/config.py b/api/config.py index 76d442e5f9..f91da883ef 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', @@ -64,7 +65,8 @@ DEFAULTS = { 'KEYWORD_STORE': 'jieba', 'BATCH_UPLOAD_LIMIT': 20, 'CODE_EXECUTION_ENDPOINT': '', - 'CODE_EXECUTION_API_KEY': '' + 'CODE_EXECUTION_API_KEY': '', + 'TOOL_ICON_CACHE_MAX_AGE': 3600, } @@ -186,6 +188,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') @@ -311,6 +314,7 @@ class Config: self.CODE_EXECUTION_API_KEY = get_env('CODE_EXECUTION_API_KEY') 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 a44105537c..5d499c0d07 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 @@ -81,7 +81,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/core/agent/base_agent_runner.py b/api/core/agent/base_agent_runner.py index 14602a7265..b93ac56916 100644 --- a/api/core/agent/base_agent_runner.py +++ b/api/core/agent/base_agent_runner.py @@ -143,7 +143,7 @@ class BaseAgentRunner(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}." @@ -497,7 +497,11 @@ class BaseAgentRunner(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()) @@ -530,4 +534,4 @@ class BaseAgentRunner(AppRunner): db.session.close() - return result \ No newline at end of file + return result diff --git a/api/core/agent/cot_agent_runner.py b/api/core/agent/cot_agent_runner.py index 0c5399f541..81e082909d 100644 --- a/api/core/agent/cot_agent_runner.py +++ b/api/core/agent/cot_agent_runner.py @@ -191,7 +191,7 @@ class CotAgentRunner(BaseAgentRunner): 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 ) @@ -689,4 +689,4 @@ class CotAgentRunner(BaseAgentRunner): 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) 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 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, 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 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 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() 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', '') 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]) + '...' 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) 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') 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') diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 8f726f3935..2202361422 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -343,8 +343,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 @@ + 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/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')} diff --git a/web/app/components/app/chat/index.tsx b/web/app/components/app/chat/index.tsx index 3ff0a5fdc3..e1fe5589ed 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 (
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 (
= ({ 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}
)) } @@ -212,9 +212,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 538c621119..9abc3c5727 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', @@ -248,7 +408,7 @@ const translation = { }, appModes: { completionApp: 'Gerador de Texto', - chatApp: 'Aplicativo de Chat', + chatApp: 'Aplicativo de Bate-papo', }, datasetMenus: { documents: 'Documentos', @@ -256,7 +416,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...', @@ -268,10 +428,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: { @@ -281,29 +441,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: { @@ -329,17 +489,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: '已使用',