From 1e03c97663a2df7d23831ff5cf2862d1ca8b2faa Mon Sep 17 00:00:00 2001 From: -LAN- Date: Wed, 11 Jun 2025 18:56:07 +0800 Subject: [PATCH 01/15] fix(llm_node): missing parameters for structure outputs (#20915) Signed-off-by: -LAN- --- api/core/workflow/nodes/llm/node.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/api/core/workflow/nodes/llm/node.py b/api/core/workflow/nodes/llm/node.py index ead929252c..d27124d62c 100644 --- a/api/core/workflow/nodes/llm/node.py +++ b/api/core/workflow/nodes/llm/node.py @@ -525,6 +525,8 @@ class LLMNode(BaseNode[LLMNodeData]): # Set appropriate response format based on model capabilities self._set_response_format(completion_params, model_schema.parameter_rules) model_config_with_cred.parameters = completion_params + # NOTE(-LAN-): This line modify the `self.node_data.model`, which is used in `_invoke_llm()`. + node_data_model.completion_params = completion_params return model, model_config_with_cred def _fetch_prompt_messages( From af83120832224f87e087c8152b66fa44077e1c3c Mon Sep 17 00:00:00 2001 From: Takuya Ono Date: Thu, 12 Jun 2025 01:49:38 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=F0=9F=90=9B=20Fix(Gemini=20LLM):=20Suppo?= =?UTF-8?q?rt=20Gemini=200.2.x=20plugin=20on=20agent=20app=20(#20794)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: QuantumGhost --- api/core/app/apps/base_app_runner.py | 21 +++++++++++++++---- .../easy_ui_based_generate_task_pipeline.py | 18 ++++++++++++++++ 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/api/core/app/apps/base_app_runner.py b/api/core/app/apps/base_app_runner.py index c813dbb9d1..a3f0cf7f9f 100644 --- a/api/core/app/apps/base_app_runner.py +++ b/api/core/app/apps/base_app_runner.py @@ -1,3 +1,4 @@ +import logging import time from collections.abc import Generator, Mapping, Sequence from typing import TYPE_CHECKING, Any, Optional, Union @@ -33,6 +34,8 @@ from models.model import App, AppMode, Message, MessageAnnotation if TYPE_CHECKING: from core.file.models import File +_logger = logging.getLogger(__name__) + class AppRunner: def get_pre_calculate_rest_tokens( @@ -298,7 +301,7 @@ class AppRunner: ) def _handle_invoke_result_stream( - self, invoke_result: Generator, queue_manager: AppQueueManager, agent: bool + self, invoke_result: Generator[LLMResultChunk, None, None], queue_manager: AppQueueManager, agent: bool ) -> None: """ Handle invoke result @@ -317,18 +320,28 @@ class AppRunner: else: queue_manager.publish(QueueAgentMessageEvent(chunk=result), PublishFrom.APPLICATION_MANAGER) - text += result.delta.message.content + message = result.delta.message + if isinstance(message.content, str): + text += message.content + elif isinstance(message.content, list): + for content in message.content: + if not isinstance(content, str): + # TODO(QuantumGhost): Add multimodal output support for easy ui. + _logger.warning("received multimodal output, type=%s", type(content)) + text += content.data + else: + text += content # failback to str if not model: model = result.model if not prompt_messages: - prompt_messages = result.prompt_messages + prompt_messages = list(result.prompt_messages) if result.delta.usage: usage = result.delta.usage - if not usage: + if usage is None: usage = LLMUsage.empty_usage() llm_result = LLMResult( diff --git a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py index 1ea50a5778..d535e1f835 100644 --- a/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py +++ b/api/core/app/task_pipeline/easy_ui_based_generate_task_pipeline.py @@ -48,6 +48,7 @@ from core.model_manager import ModelInstance from core.model_runtime.entities.llm_entities import LLMResult, LLMResultChunk, LLMResultChunkDelta, LLMUsage from core.model_runtime.entities.message_entities import ( AssistantPromptMessage, + TextPromptMessageContent, ) from core.model_runtime.model_providers.__base.large_language_model import LargeLanguageModel from core.ops.entities.trace_entity import TraceTaskName @@ -309,6 +310,23 @@ class EasyUIBasedGenerateTaskPipeline(BasedGenerateTaskPipeline): delta_text = chunk.delta.message.content if delta_text is None: continue + if isinstance(chunk.delta.message.content, list): + delta_text = "" + for content in chunk.delta.message.content: + logger.debug( + "The content type %s in LLM chunk delta message content.: %r", type(content), content + ) + if isinstance(content, TextPromptMessageContent): + delta_text += content.data + elif isinstance(content, str): + delta_text += content # failback to str + else: + logger.warning( + "Unsupported content type %s in LLM chunk delta message content.: %r", + type(content), + content, + ) + continue if not self._task_state.llm_result.prompt_messages: self._task_state.llm_result.prompt_messages = chunk.prompt_messages From b2ac11bc47b77e4a2dede3799fe34cc15ce9deda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Thu, 12 Jun 2025 14:18:15 +0800 Subject: [PATCH 03/15] fix: markdown button can't send message (#20933) --- web/app/components/base/markdown-blocks/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/app/components/base/markdown-blocks/button.tsx b/web/app/components/base/markdown-blocks/button.tsx index 4646b12921..315653bcd0 100644 --- a/web/app/components/base/markdown-blocks/button.tsx +++ b/web/app/components/base/markdown-blocks/button.tsx @@ -14,7 +14,7 @@ const MarkdownButton = ({ node }: any) => { size={size} className={cn('!h-auto min-h-8 select-none whitespace-normal !px-3')} onClick={() => { - if (isValidUrl(link)) { + if (link && isValidUrl(link)) { window.open(link, '_blank') return } From c05e47ebc0017caba71f1787a10523a96070a53f Mon Sep 17 00:00:00 2001 From: -LAN- Date: Fri, 13 Jun 2025 09:42:02 +0800 Subject: [PATCH 04/15] refactor(sqlalchemy_workflow_execution_repository): Use the max funtion for getting next_sequence_number. (#20966) --- .../sqlalchemy_workflow_execution_repository.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/core/repositories/sqlalchemy_workflow_execution_repository.py b/api/core/repositories/sqlalchemy_workflow_execution_repository.py index 19086cffff..e5ead9dc56 100644 --- a/api/core/repositories/sqlalchemy_workflow_execution_repository.py +++ b/api/core/repositories/sqlalchemy_workflow_execution_repository.py @@ -6,7 +6,7 @@ import json import logging from typing import Optional, Union -from sqlalchemy import select +from sqlalchemy import func, select from sqlalchemy.engine import Engine from sqlalchemy.orm import sessionmaker @@ -151,11 +151,11 @@ class SQLAlchemyWorkflowExecutionRepository(WorkflowExecutionRepository): existing = session.scalar(select(WorkflowRun).where(WorkflowRun.id == domain_model.id_)) if not existing: # For new records, get the next sequence number - stmt = select(WorkflowRun.sequence_number).where( + stmt = select(func.max(WorkflowRun.sequence_number)).where( WorkflowRun.app_id == self._app_id, WorkflowRun.tenant_id == self._tenant_id, ) - max_sequence = session.scalar(stmt.order_by(WorkflowRun.sequence_number.desc())) + max_sequence = session.scalar(stmt) db_model.sequence_number = (max_sequence or 0) + 1 else: # For updates, keep the existing sequence number From 8cb3ed5cc2d51ddb391dfc80cdc96d2b71556d3a Mon Sep 17 00:00:00 2001 From: "Byron.wang" Date: Fri, 13 Jun 2025 15:05:55 +0800 Subject: [PATCH 05/15] feat: add S3_USE_AWS env var to explicitly distinguish AWS S3 usage in plugin-daemon (#20923) --- docker/.env.example | 1 + docker/docker-compose-template.yaml | 1 + docker/docker-compose.middleware.yaml | 1 + docker/docker-compose.yaml | 2 ++ docker/middleware.env.example | 1 + 5 files changed, 6 insertions(+) diff --git a/docker/.env.example b/docker/.env.example index 4cf5e202d0..d4d59936eb 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -1067,6 +1067,7 @@ PLUGIN_MEDIA_CACHE_PATH=assets # Plugin oss bucket PLUGIN_STORAGE_OSS_BUCKET= # Plugin oss s3 credentials +PLUGIN_S3_USE_AWS= PLUGIN_S3_USE_AWS_MANAGED_IAM=false PLUGIN_S3_ENDPOINT= PLUGIN_S3_USE_PATH_STYLE=false diff --git a/docker/docker-compose-template.yaml b/docker/docker-compose-template.yaml index a409a729ce..648b82c918 100644 --- a/docker/docker-compose-template.yaml +++ b/docker/docker-compose-template.yaml @@ -168,6 +168,7 @@ services: PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} + S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-} S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} diff --git a/docker/docker-compose.middleware.yaml b/docker/docker-compose.middleware.yaml index dceee484ca..4081bfd818 100644 --- a/docker/docker-compose.middleware.yaml +++ b/docker/docker-compose.middleware.yaml @@ -104,6 +104,7 @@ services: PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} + S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-} S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index d927334118..ec910f6258 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -467,6 +467,7 @@ x-shared-env: &shared-api-worker-env PLUGIN_PACKAGE_CACHE_PATH: ${PLUGIN_PACKAGE_CACHE_PATH:-plugin_packages} PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} + PLUGIN_S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-} PLUGIN_S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} PLUGIN_S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} PLUGIN_S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} @@ -674,6 +675,7 @@ services: PLUGIN_MEDIA_CACHE_PATH: ${PLUGIN_MEDIA_CACHE_PATH:-assets} PLUGIN_STORAGE_OSS_BUCKET: ${PLUGIN_STORAGE_OSS_BUCKET:-} S3_USE_AWS_MANAGED_IAM: ${PLUGIN_S3_USE_AWS_MANAGED_IAM:-false} + S3_USE_AWS: ${PLUGIN_S3_USE_AWS:-} S3_ENDPOINT: ${PLUGIN_S3_ENDPOINT:-} S3_USE_PATH_STYLE: ${PLUGIN_S3_USE_PATH_STYLE:-false} AWS_ACCESS_KEY: ${PLUGIN_AWS_ACCESS_KEY:-} diff --git a/docker/middleware.env.example b/docker/middleware.env.example index 66037f281c..338b057ae8 100644 --- a/docker/middleware.env.example +++ b/docker/middleware.env.example @@ -133,6 +133,7 @@ PLUGIN_MEDIA_CACHE_PATH=assets PLUGIN_STORAGE_OSS_BUCKET= # Plugin oss s3 credentials PLUGIN_S3_USE_AWS_MANAGED_IAM=false +PLUGIN_S3_USE_AWS= PLUGIN_S3_ENDPOINT= PLUGIN_S3_USE_PATH_STYLE=false PLUGIN_AWS_ACCESS_KEY= From 175571e74028043bada19135f52fe4a6293ad974 Mon Sep 17 00:00:00 2001 From: sayThQ199 <18852951350@163.com> Date: Fri, 13 Jun 2025 16:17:12 +0800 Subject: [PATCH 06/15] fix(auth): Clear login rate limit after password reset (#20948) --- api/commands.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/commands.py b/api/commands.py index 6262bfde6b..0a6cc61a68 100644 --- a/api/commands.py +++ b/api/commands.py @@ -27,7 +27,7 @@ from models.dataset import Dataset, DatasetCollectionBinding, DatasetMetadata, D from models.dataset import Document as DatasetDocument from models.model import Account, App, AppAnnotationSetting, AppMode, Conversation, MessageAnnotation from models.provider import Provider, ProviderModel -from services.account_service import RegisterService, TenantService +from services.account_service import AccountService, RegisterService, TenantService from services.clear_free_plan_tenant_expired_logs import ClearFreePlanTenantExpiredLogs from services.plugin.data_migration import PluginDataMigration from services.plugin.plugin_migration import PluginMigration @@ -68,6 +68,7 @@ def reset_password(email, new_password, password_confirm): account.password = base64_password_hashed account.password_salt = base64_salt db.session.commit() + AccountService.reset_login_error_rate_limit(email) click.echo(click.style("Password reset successfully.", fg="green")) From 3a628bc671b4e0b13648ee575d619633134d9266 Mon Sep 17 00:00:00 2001 From: quicksand Date: Fri, 13 Jun 2025 16:17:35 +0800 Subject: [PATCH 07/15] chore: app info add author_name (#20973) --- api/controllers/service_api/app/app.py | 8 +++++++- web/app/components/develop/template/template.en.mdx | 6 +++++- web/app/components/develop/template/template.ja.mdx | 6 +++++- web/app/components/develop/template/template.zh.mdx | 6 +++++- .../develop/template/template_advanced_chat.en.mdx | 6 +++++- .../develop/template/template_advanced_chat.ja.mdx | 6 +++++- .../develop/template/template_advanced_chat.zh.mdx | 4 +++- web/app/components/develop/template/template_chat.en.mdx | 6 +++++- web/app/components/develop/template/template_chat.ja.mdx | 6 +++++- web/app/components/develop/template/template_chat.zh.mdx | 6 +++++- .../components/develop/template/template_workflow.en.mdx | 6 +++++- .../components/develop/template/template_workflow.ja.mdx | 6 +++++- .../components/develop/template/template_workflow.zh.mdx | 6 +++++- 13 files changed, 65 insertions(+), 13 deletions(-) diff --git a/api/controllers/service_api/app/app.py b/api/controllers/service_api/app/app.py index 2c03aba33d..89222d5e83 100644 --- a/api/controllers/service_api/app/app.py +++ b/api/controllers/service_api/app/app.py @@ -47,7 +47,13 @@ class AppInfoApi(Resource): def get(self, app_model: App): """Get app information""" tags = [tag.name for tag in app_model.tags] - return {"name": app_model.name, "description": app_model.description, "tags": tags, "mode": app_model.mode} + return { + "name": app_model.name, + "description": app_model.description, + "tags": tags, + "mode": app_model.mode, + "author_name": app_model.author_name, + } api.add_resource(AppParameterApi, "/parameters") diff --git a/web/app/components/develop/template/template.en.mdx b/web/app/components/develop/template/template.en.mdx index 9682b51b39..f1b0b60dda 100755 --- a/web/app/components/develop/template/template.en.mdx +++ b/web/app/components/develop/template/template.en.mdx @@ -512,6 +512,8 @@ The text generation application offers non-session support and is ideal for tran - `name` (string) application name - `description` (string) application description - `tags` (array[string]) application tags + - `mode` (string) application mode + - `author_name` (string) author name @@ -528,7 +530,9 @@ The text generation application offers non-session support and is ideal for tran "tags": [ "tag1", "tag2" - ] + ], + "mode": "chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template.ja.mdx b/web/app/components/develop/template/template.ja.mdx index fc6291f522..2cbd87cf36 100755 --- a/web/app/components/develop/template/template.ja.mdx +++ b/web/app/components/develop/template/template.ja.mdx @@ -510,6 +510,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `name` (string) アプリケーションの名前 - `description` (string) アプリケーションの説明 - `tags` (array[string]) アプリケーションのタグ + - `mode` (string) アプリケーションのモード + - `author_name` (string) 作者の名前 @@ -526,7 +528,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from "tags": [ "tag1", "tag2" - ] + ], + "mode": "chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx index 9e65a4bd9b..64740d8930 100755 --- a/web/app/components/develop/template/template.zh.mdx +++ b/web/app/components/develop/template/template.zh.mdx @@ -485,6 +485,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `name` (string) 应用名称 - `description` (string) 应用描述 - `tags` (array[string]) 应用标签 + - `mode` (string) 应用模式 + - 'author_name' (string) 作者名称 @@ -501,7 +503,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' "tags": [ "tag1", "tag2" - ] + ], + "mode": "chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 76e336bdf4..3a9ef73b04 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -1123,6 +1123,8 @@ Chat applications support session persistence, allowing previous chat history to - `name` (string) application name - `description` (string) application description - `tags` (array[string]) application tags + - `mode` (string) application mode + - `author_name` (string) application author name @@ -1139,7 +1141,9 @@ Chat applications support session persistence, allowing previous chat history to "tags": [ "tag1", "tag2" - ] + ], + "mode": "advanced-chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index 6f94cbb2c7..72c5885045 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -1123,6 +1123,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `name` (string) アプリケーションの名前 - `description` (string) アプリケーションの説明 - `tags` (array[string]) アプリケーションのタグ + - `mode` (string) アプリケーションのモード + - `author_name` (string) 作者の名前 @@ -1139,7 +1141,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from "tags": [ "tag1", "tag2" - ] + ], + "mode": "advanced-chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index 3e268d6e65..7559dd507c 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -1173,7 +1173,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' "tags": [ "tag1", "tag2" - ] + ], + "mode": "advanced-chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index f0cb3cd513..e1fdeb690e 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -1151,6 +1151,8 @@ Chat applications support session persistence, allowing previous chat history to - `name` (string) application name - `description` (string) application description - `tags` (array[string]) application tags + - `mode` (string) application mode + - `author_name` (string) application author name @@ -1167,7 +1169,9 @@ Chat applications support session persistence, allowing previous chat history to "tags": [ "tag1", "tag2" - ] + ], + "mode": "advanced-chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_chat.ja.mdx b/web/app/components/develop/template/template_chat.ja.mdx index a1b2ae5f1d..e62ff4ff42 100644 --- a/web/app/components/develop/template/template_chat.ja.mdx +++ b/web/app/components/develop/template/template_chat.ja.mdx @@ -1150,6 +1150,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `name` (string) アプリケーションの名前 - `description` (string) アプリケーションの説明 - `tags` (array[string]) アプリケーションのタグ + - `mode` (string) アプリケーションのモード + - `author_name` (string) 作者の名前 @@ -1166,7 +1168,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from "tags": [ "tag1", "tag2" - ] + ], + "mode": "chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index 9c1a168bf5..f6714c95f0 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -1162,6 +1162,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - `name` (string) 应用名称 - `description` (string) 应用描述 - `tags` (array[string]) 应用标签 + - `mode` (string) 应用模式 + - 'author_name' (string) 作者名称 @@ -1178,7 +1180,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' "tags": [ "tag1", "tag2" - ] + ], + "mode": "chat", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_workflow.en.mdx b/web/app/components/develop/template/template_workflow.en.mdx index 95e4971e67..9aa5d60c57 100644 --- a/web/app/components/develop/template/template_workflow.en.mdx +++ b/web/app/components/develop/template/template_workflow.en.mdx @@ -625,6 +625,8 @@ Workflow applications offers non-session support and is ideal for translation, a - `name` (string) application name - `description` (string) application description - `tags` (array[string]) application tags + - `mode` (string) application mode + - `author_name` (string) application author name @@ -641,7 +643,9 @@ Workflow applications offers non-session support and is ideal for translation, a "tags": [ "tag1", "tag2" - ] + ], + "mode": "workflow", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_workflow.ja.mdx b/web/app/components/develop/template/template_workflow.ja.mdx index ab53d05e81..e957de2872 100644 --- a/web/app/components/develop/template/template_workflow.ja.mdx +++ b/web/app/components/develop/template/template_workflow.ja.mdx @@ -628,6 +628,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - `name` (string) アプリケーションの名前 - `description` (string) アプリケーションの説明 - `tags` (array[string]) アプリケーションのタグ + - `mode` (string) アプリケーションのモード + - `author_name` (string) 作者の名前 @@ -644,7 +646,9 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from "tags": [ "tag1", "tag2" - ] + ], + "mode": "workflow", + "author_name": "Dify" } ``` diff --git a/web/app/components/develop/template/template_workflow.zh.mdx b/web/app/components/develop/template/template_workflow.zh.mdx index fe59988eda..f8b05fea19 100644 --- a/web/app/components/develop/template/template_workflow.zh.mdx +++ b/web/app/components/develop/template/template_workflow.zh.mdx @@ -615,6 +615,8 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 - `name` (string) 应用名称 - `description` (string) 应用描述 - `tags` (array[string]) 应用标签 + - `mode` (string) 应用模式 + - 'author_name' (string) 作者名称 @@ -631,7 +633,9 @@ Workflow 应用无会话支持,适合用于翻译/文章写作/总结 AI 等 "tags": [ "tag1", "tag2" - ] + ], + "mode": "workflow", + "author_name": "Dify" } ``` From f4df759ba6282b0c0c0f9e03b8dac171cdba8318 Mon Sep 17 00:00:00 2001 From: Bowen Liang Date: Fri, 13 Jun 2025 16:58:43 +0800 Subject: [PATCH 08/15] refactor: generalize method for getting doc link respecting locale and fix error link paths (#20801) --- .../[datasetId]/layout-main.tsx | 11 +++-------- .../conversation-history/history-panel.tsx | 11 ++++------- .../dataset-config/settings-modal/index.tsx | 4 +++- .../prompt-mode/advanced-mode-waring.tsx | 8 +++----- .../components/app/create-app-modal/index.tsx | 13 ++++++++----- .../app/overview/customize/index.tsx | 11 +++-------- .../app/overview/settings/index.tsx | 10 +++++----- .../base/features/new-feature-panel/index.tsx | 8 +++----- .../datasets/create/step-two/index.tsx | 6 +++++- .../create/website/base/url-input.tsx | 4 +++- .../website/jina-reader/base/url-input.tsx | 4 +++- .../components/datasets/documents/index.tsx | 11 +++-------- .../external-api/external-api-modal/Form.tsx | 4 +++- .../external-api/external-api-panel/index.tsx | 5 ++++- .../create/InfoPanel.tsx | 9 +++++++-- .../external-knowledge-base/create/index.tsx | 4 +++- .../hit-testing/modify-retrieval-modal.tsx | 4 +++- .../datasets/settings/form/index.tsx | 4 +++- .../header/account-dropdown/index.tsx | 6 +++--- .../plugin-detail-panel/endpoint-list.tsx | 8 +++----- .../tools/provider/custom-create-card.tsx | 8 +++++--- .../nodes/_base/components/agent-strategy.tsx | 15 ++++++--------- .../components/error-handle/default-value.tsx | 4 +++- .../error-handle/fail-branch-card.tsx | 4 +++- .../variable/var-reference-popup.tsx | 12 ++++++------ .../nodes/_base/hooks/use-node-help-link.ts | 8 +++----- .../json-schema-config.tsx | 6 +++--- .../panel/chat-variable-panel/index.tsx | 13 +++++++------ web/app/components/workflow/run/node.tsx | 4 +++- web/app/components/workflow/run/status.tsx | 4 +++- .../education-apply/education-apply-page.tsx | 16 +++------------- .../education-apply/verify-state-modal.tsx | 19 ++++++------------- web/app/install/installForm.tsx | 4 +++- web/app/signin/invite-settings/page.tsx | 6 ++++-- web/app/signin/oneMoreStep.tsx | 4 +++- web/context/i18n.ts | 13 +++++++++++++ 36 files changed, 149 insertions(+), 136 deletions(-) diff --git a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx index 94cd5ad562..fb3a9087ca 100644 --- a/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx +++ b/web/app/(commonLayout)/datasets/(datasetDetailLayout)/[datasetId]/layout-main.tsx @@ -25,9 +25,8 @@ import Loading from '@/app/components/base/loading' import DatasetDetailContext from '@/context/dataset-detail' import { DataSourceType } from '@/models/datasets' import useBreakpoints, { MediaType } from '@/hooks/use-breakpoints' -import { LanguagesSupported } from '@/i18n/language' import { useStore } from '@/app/components/app/store' -import { getLocaleOnClient } from '@/i18n' +import { useDocLink } from '@/context/i18n' import { useAppContext } from '@/context/app-context' import Tooltip from '@/app/components/base/tooltip' import LinkedAppsPanel from '@/app/components/base/linked-apps-panel' @@ -45,9 +44,9 @@ type IExtraInfoProps = { } const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => { - const locale = getLocaleOnClient() const [isShowTips, { toggle: toggleTips, set: setShowTips }] = useBoolean(!isMobile) const { t } = useTranslation() + const docLink = useDocLink() const hasRelatedApps = relatedApps?.data && relatedApps?.data?.length > 0 const relatedAppsTotal = relatedApps?.data?.length || 0 @@ -97,11 +96,7 @@ const ExtraInfo = ({ isMobile, relatedApps, expand }: IExtraInfoProps) => {
{t('common.datasetMenus.emptyTip')}
diff --git a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx index 592c95261c..94a02945bb 100644 --- a/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx +++ b/web/app/components/app/configuration/config-prompt/conversation-history/history-panel.tsx @@ -1,13 +1,11 @@ 'use client' import type { FC } from 'react' import React from 'react' -import { useContext } from 'use-context-selector' import { useTranslation } from 'react-i18next' import OperationBtn from '@/app/components/app/configuration/base/operation-btn' import Panel from '@/app/components/app/configuration/base/feature-panel' import { MessageClockCircle } from '@/app/components/base/icons/src/vender/solid/general' -import I18n from '@/context/i18n' -import { LanguagesSupported } from '@/i18n/language' +import { useDocLink } from '@/context/i18n' type Props = { showWarning: boolean @@ -19,7 +17,7 @@ const HistoryPanel: FC = ({ onShowEditModal, }) => { const { t } = useTranslation() - const { locale } = useContext(I18n) + const docLink = useDocLink() return ( = ({ {showWarning && (
{t('appDebug.feature.conversationHistory.tip')} - {t('appDebug.feature.conversationHistory.learnMore')} diff --git a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx index 3170d33a82..9835481ae0 100644 --- a/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx +++ b/web/app/components/app/configuration/dataset-config/settings-modal/index.tsx @@ -31,6 +31,7 @@ import { import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { fetchMembers } from '@/service/common' import type { Member } from '@/models/common' +import { useDocLink } from '@/context/i18n' type SettingsModalProps = { currentDataset: DataSet @@ -58,6 +59,7 @@ const SettingsModal: FC = ({ currentModel: isRerankDefaultModelValid, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) const { t } = useTranslation() + const docLink = useDocLink() const { notify } = useToastContext() const ref = useRef(null) const isExternal = currentDataset.provider === 'external' @@ -328,7 +330,7 @@ const SettingsModal: FC = ({
{t('datasetSettings.form.retrievalSetting.title')}
diff --git a/web/app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx b/web/app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx index cca775c86e..f207cddd16 100644 --- a/web/app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx +++ b/web/app/components/app/configuration/prompt-mode/advanced-mode-waring.tsx @@ -2,9 +2,7 @@ import type { FC } from 'react' import React from 'react' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' -import I18n from '@/context/i18n' -import { LanguagesSupported } from '@/i18n/language' +import { useDocLink } from '@/context/i18n' type Props = { onReturnToSimpleMode: () => void } @@ -13,7 +11,7 @@ const AdvancedModeWarning: FC = ({ onReturnToSimpleMode, }) => { const { t } = useTranslation() - const { locale } = useContext(I18n) + const docLink = useDocLink() const [show, setShow] = React.useState(true) if (!show) return null @@ -25,7 +23,7 @@ const AdvancedModeWarning: FC = ({ {t('appDebug.promptMode.advancedWarning.description')} {t('appDebug.promptMode.advancedWarning.learnMore')} diff --git a/web/app/components/app/create-app-modal/index.tsx b/web/app/components/app/create-app-modal/index.tsx index 6e5547d08a..bfb7c43c0d 100644 --- a/web/app/components/app/create-app-modal/index.tsx +++ b/web/app/components/app/create-app-modal/index.tsx @@ -29,6 +29,7 @@ import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { getRedirection } from '@/utils/app-redirection' import FullScreenModal from '@/app/components/base/fullscreen-modal' import useTheme from '@/hooks/use-theme' +import { useDocLink } from '@/context/i18n' type CreateAppProps = { onSuccess: () => void @@ -303,31 +304,33 @@ function AppTypeCard({ icon, title, description, active, onClick }: AppTypeCardP function AppPreview({ mode }: { mode: AppMode }) { const { t } = useTranslation() + const docLink = useDocLink() const modeToPreviewInfoMap = { 'chat': { title: t('app.types.chatbot'), description: t('app.newApp.chatbotUserDescription'), - link: 'https://docs.dify.ai/guides/application-orchestrate/readme', + link: docLink('/guides/application-orchestrate/chatbot-application'), }, 'advanced-chat': { title: t('app.types.advanced'), description: t('app.newApp.advancedUserDescription'), - link: 'https://docs.dify.ai/en/guides/workflow/README', + link: docLink('/guides/workflow/readme'), }, 'agent-chat': { title: t('app.types.agent'), description: t('app.newApp.agentUserDescription'), - link: 'https://docs.dify.ai/en/guides/application-orchestrate/agent', + link: docLink('/guides/application-orchestrate/agent'), }, 'completion': { title: t('app.newApp.completeApp'), description: t('app.newApp.completionUserDescription'), - link: null, + link: docLink('/guides/application-orchestrate/text-generator', + { 'zh-Hans': '/guides/application-orchestrate/readme' }), }, 'workflow': { title: t('app.types.workflow'), description: t('app.newApp.workflowUserDescription'), - link: 'https://docs.dify.ai/en/guides/workflow/README', + link: docLink('/guides/workflow/readme'), }, } const previewInfo = modeToPreviewInfoMap[mode] diff --git a/web/app/components/app/overview/customize/index.tsx b/web/app/components/app/overview/customize/index.tsx index 4e84dd8b1f..0fedd76f89 100644 --- a/web/app/components/app/overview/customize/index.tsx +++ b/web/app/components/app/overview/customize/index.tsx @@ -3,13 +3,11 @@ import type { FC } from 'react' import React from 'react' import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' +import { useDocLink } from '@/context/i18n' import type { AppMode } from '@/types/app' -import I18n from '@/context/i18n' import Button from '@/app/components/base/button' import Modal from '@/app/components/base/modal' import Tag from '@/app/components/base/tag' -import { LanguagesSupported } from '@/i18n/language' type IShareLinkProps = { isShow: boolean @@ -43,7 +41,7 @@ const CustomizeModal: FC = ({ mode, }) => { const { t } = useTranslation() - const { locale } = useContext(I18n) + const docLink = useDocLink() const isChatApp = mode === 'chat' || mode === 'advanced-chat' return = ({ className='mt-2' onClick={() => window.open( - `https://docs.dify.ai/${locale !== LanguagesSupported[1] - ? 'user-guide/launching-dify-apps/developing-with-apis' - : `${locale.toLowerCase()}/guides/application-publishing/developing-with-apis` - }`, + docLink('/guides/application-publishing/developing-with-apis'), '_blank', ) } diff --git a/web/app/components/app/overview/settings/index.tsx b/web/app/components/app/overview/settings/index.tsx index 98e2cab681..c2d98383c4 100644 --- a/web/app/components/app/overview/settings/index.tsx +++ b/web/app/components/app/overview/settings/index.tsx @@ -4,7 +4,6 @@ import React, { useCallback, useEffect, useState } from 'react' import { RiArrowRightSLine, RiCloseLine } from '@remixicon/react' import Link from 'next/link' import { Trans, useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' import { SparklesSoft } from '@/app/components/base/icons/src/public/common' import Modal from '@/app/components/base/modal' import ActionButton from '@/app/components/base/action-button' @@ -19,14 +18,14 @@ import { SimpleSelect } from '@/app/components/base/select' import type { AppDetailResponse } from '@/models/app' import type { AppIconType, AppSSO, Language } from '@/types/app' import { useToastContext } from '@/app/components/base/toast' -import { LanguagesSupported, languages } from '@/i18n/language' +import { languages } from '@/i18n/language' import Tooltip from '@/app/components/base/tooltip' import { useProviderContext } from '@/context/provider-context' import { useModalContext } from '@/context/modal-context' import type { AppIconSelection } from '@/app/components/base/app-icon-picker' import AppIconPicker from '@/app/components/base/app-icon-picker' -import I18n from '@/context/i18n' import cn from '@/utils/classnames' +import { useDocLink } from '@/context/i18n' export type ISettingsModalProps = { isChat: boolean @@ -98,7 +97,7 @@ const SettingsModal: FC = ({ const [language, setLanguage] = useState(default_language) const [saveLoading, setSaveLoading] = useState(false) const { t } = useTranslation() - const { locale } = useContext(I18n) + const docLink = useDocLink() const [showAppIconPicker, setShowAppIconPicker] = useState(false) const [appIcon, setAppIcon] = useState( @@ -238,7 +237,8 @@ const SettingsModal: FC = ({
{t(`${prefixSettings}.modalTip`)} - {t('common.operation.learnMore')} + {t('common.operation.learnMore')}
{/* form body */} diff --git a/web/app/components/base/features/new-feature-panel/index.tsx b/web/app/components/base/features/new-feature-panel/index.tsx index eee680596b..d00cd9038a 100644 --- a/web/app/components/base/features/new-feature-panel/index.tsx +++ b/web/app/components/base/features/new-feature-panel/index.tsx @@ -1,6 +1,5 @@ import React from 'react' import { useTranslation } from 'react-i18next' -import { useContext } from 'use-context-selector' import { RiCloseLine, RiInformation2Fill } from '@remixicon/react' import DialogWrapper from '@/app/components/base/features/new-feature-panel/dialog-wrapper' import { useDefaultModel } from '@/app/components/header/account-setting/model-provider-page/hooks' @@ -19,8 +18,7 @@ import Moderation from '@/app/components/base/features/new-feature-panel/moderat import AnnotationReply from '@/app/components/base/features/new-feature-panel/annotation-reply' import type { PromptVariable } from '@/models/debug' import type { InputVar } from '@/app/components/workflow/types' -import I18n from '@/context/i18n' -import { LanguagesSupported } from '@/i18n/language' +import { useDocLink } from '@/context/i18n' type Props = { show: boolean @@ -48,7 +46,7 @@ const NewFeaturePanel = ({ onAutoAddPromptVariable, }: Props) => { const { t } = useTranslation() - const { locale } = useContext(I18n) + const docLink = useDocLink() const { data: speech2textDefaultModel } = useDefaultModel(ModelTypeEnum.speech2text) const { data: text2speechDefaultModel } = useDefaultModel(ModelTypeEnum.tts) @@ -80,7 +78,7 @@ const NewFeaturePanel = ({ {isChatMode ? t('workflow.common.fileUploadTip') : t('workflow.common.ImageUploadLegacyTip')} {t('workflow.common.featuresDocLink')} diff --git a/web/app/components/datasets/create/step-two/index.tsx b/web/app/components/datasets/create/step-two/index.tsx index 52c5195bbf..c931addd1a 100644 --- a/web/app/components/datasets/create/step-two/index.tsx +++ b/web/app/components/datasets/create/step-two/index.tsx @@ -63,6 +63,7 @@ import CustomDialog from '@/app/components/base/dialog' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' import { AlertTriangle } from '@/app/components/base/icons/src/vender/solid/alertsAndFeedback' import { noop } from 'lodash-es' +import { useDocLink } from '@/context/i18n' const TextLabel: FC = (props) => { return @@ -146,6 +147,7 @@ const StepTwo = ({ updateRetrievalMethodCache, }: StepTwoProps) => { const { t } = useTranslation() + const docLink = useDocLink() const { locale } = useContext(I18n) const media = useBreakpoints() const isMobile = media === MediaType.mobile @@ -962,7 +964,9 @@ const StepTwo = ({
{t('datasetSettings.form.retrievalSetting.title')}
diff --git a/web/app/components/datasets/create/website/base/url-input.tsx b/web/app/components/datasets/create/website/base/url-input.tsx index b7dc9bfca5..ab965bebc3 100644 --- a/web/app/components/datasets/create/website/base/url-input.tsx +++ b/web/app/components/datasets/create/website/base/url-input.tsx @@ -4,6 +4,7 @@ import React, { useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import Input from './input' import Button from '@/app/components/base/button' +import { useDocLink } from '@/context/i18n' const I18N_PREFIX = 'datasetCreation.stepOne.website' @@ -17,6 +18,7 @@ const UrlInput: FC = ({ onRun, }) => { const { t } = useTranslation() + const docLink = useDocLink() const [url, setUrl] = useState('') const handleUrlChange = useCallback((url: string | number) => { setUrl(url as string) @@ -32,7 +34,7 @@ const UrlInput: FC = ({