From 0e4f19eee01393da757b583c87f0701c2009b2de Mon Sep 17 00:00:00 2001 From: Timo <57227498+EchterTimo@users.noreply.github.com> Date: Sat, 27 Sep 2025 15:03:05 +0200 Subject: [PATCH 001/123] Fix ChatClient.audio_to_text files keyword to make it work (#26317) Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- sdks/python-client/dify_client/client.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdks/python-client/dify_client/client.py b/sdks/python-client/dify_client/client.py index 791cb98a1b..201391eae9 100644 --- a/sdks/python-client/dify_client/client.py +++ b/sdks/python-client/dify_client/client.py @@ -139,9 +139,9 @@ class ChatClient(DifyClient): data = {"user": user} return self._send_request("DELETE", f"/conversations/{conversation_id}", data) - def audio_to_text(self, audio_file: dict, user: str): + def audio_to_text(self, audio_file: IO[bytes] | tuple, user: str): data = {"user": user} - files = {"audio_file": audio_file} + files = {"file": audio_file} return self._send_request_with_files("POST", "/audio-to-text", data, files) From 043ec46c331b4ad82d73916f7a9491ecc8d877fb Mon Sep 17 00:00:00 2001 From: "Junyan Qin (Chin)" Date: Sun, 28 Sep 2025 10:26:11 +0800 Subject: [PATCH 002/123] perf: distribute concurrent plugin auto upgrade tasks (#26282) --- api/schedule/check_upgradable_plugin_task.py | 33 ++++++++++++++------ 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/api/schedule/check_upgradable_plugin_task.py b/api/schedule/check_upgradable_plugin_task.py index 08a5cfce79..a9ad27b059 100644 --- a/api/schedule/check_upgradable_plugin_task.py +++ b/api/schedule/check_upgradable_plugin_task.py @@ -1,3 +1,4 @@ +import math import time import click @@ -8,6 +9,7 @@ from models.account import TenantPluginAutoUpgradeStrategy from tasks.process_tenant_plugin_autoupgrade_check_task import process_tenant_plugin_autoupgrade_check_task AUTO_UPGRADE_MINIMAL_CHECKING_INTERVAL = 15 * 60 # 15 minutes +MAX_CONCURRENT_CHECK_TASKS = 20 @app.celery.task(queue="plugin") @@ -30,15 +32,28 @@ def check_upgradable_plugin_task(): .all() ) - for strategy in strategies: - process_tenant_plugin_autoupgrade_check_task.delay( - strategy.tenant_id, - strategy.strategy_setting, - strategy.upgrade_time_of_day, - strategy.upgrade_mode, - strategy.exclude_plugins, - strategy.include_plugins, - ) + total_strategies = len(strategies) + click.echo(click.style(f"Total strategies: {total_strategies}", fg="green")) + + batch_chunk_count = math.ceil( + total_strategies / MAX_CONCURRENT_CHECK_TASKS + ) # make sure all strategies are checked in this interval + batch_interval_time = (AUTO_UPGRADE_MINIMAL_CHECKING_INTERVAL / batch_chunk_count) if batch_chunk_count > 0 else 0 + + for i in range(0, total_strategies, MAX_CONCURRENT_CHECK_TASKS): + batch_strategies = strategies[i : i + MAX_CONCURRENT_CHECK_TASKS] + for strategy in batch_strategies: + process_tenant_plugin_autoupgrade_check_task.delay( + strategy.tenant_id, + strategy.strategy_setting, + strategy.upgrade_time_of_day, + strategy.upgrade_mode, + strategy.exclude_plugins, + strategy.include_plugins, + ) + + if batch_interval_time > 0.0001: # if lower than 1ms, skip + time.sleep(batch_interval_time) end_at = time.perf_counter() click.echo( From 244c132656c50bd7e8f88ac049ee8b6499477188 Mon Sep 17 00:00:00 2001 From: goofy <38034027+goofy-z@users.noreply.github.com> Date: Sun, 28 Sep 2025 10:44:20 +0800 Subject: [PATCH 003/123] fix compatibility problem caused by tool node attribute 'tool_node_version' judgement error (#26274) --- api/core/workflow/nodes/agent/agent_node.py | 2 +- api/core/workflow/nodes/tool/tool_node.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/workflow/nodes/agent/agent_node.py b/api/core/workflow/nodes/agent/agent_node.py index ec05805879..a01686a4b8 100644 --- a/api/core/workflow/nodes/agent/agent_node.py +++ b/api/core/workflow/nodes/agent/agent_node.py @@ -288,7 +288,7 @@ class AgentNode(Node): # But for backward compatibility with historical data # this version field judgment is still preserved here. runtime_variable_pool: VariablePool | None = None - if node_data.version != "1" or node_data.tool_node_version != "1": + if node_data.version != "1" or node_data.tool_node_version is not None: runtime_variable_pool = variable_pool tool_runtime = ToolManager.get_agent_tool_runtime( self.tenant_id, self.app_id, entity, self.invoke_from, runtime_variable_pool diff --git a/api/core/workflow/nodes/tool/tool_node.py b/api/core/workflow/nodes/tool/tool_node.py index 5f2abcd378..ce1a879ff1 100644 --- a/api/core/workflow/nodes/tool/tool_node.py +++ b/api/core/workflow/nodes/tool/tool_node.py @@ -79,7 +79,7 @@ class ToolNode(Node): # But for backward compatibility with historical data # this version field judgment is still preserved here. variable_pool: VariablePool | None = None - if node_data.version != "1" or node_data.tool_node_version != "1": + if node_data.version != "1" or node_data.tool_node_version is not None: variable_pool = self.graph_runtime_state.variable_pool tool_runtime = ToolManager.get_workflow_tool_runtime( self.tenant_id, self.app_id, self._node_id, self._node_data, self.invoke_from, variable_pool From 095c56a646943bb3eeaa6aa091d3dccb1e0b7edb Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 28 Sep 2025 13:37:06 +0800 Subject: [PATCH 004/123] refactor(router): apply ns.route style (#26339) --- api/controllers/console/__init__.py | 122 +++--------------- api/controllers/console/app/app_import.py | 5 + .../console/auth/data_source_bearer_auth.py | 10 +- .../console/auth/email_register.py | 10 +- .../console/auth/forgot_password.py | 5 - api/controllers/console/auth/login.py | 16 +-- api/controllers/console/auth/oauth_server.py | 12 +- api/controllers/console/billing/billing.py | 8 +- api/controllers/console/billing/compliance.py | 6 +- .../console/datasets/data_source.py | 26 ++-- .../console/datasets/datasets_document.py | 27 +--- .../console/datasets/datasets_segments.py | 42 +++--- api/controllers/console/datasets/metadata.py | 14 +- .../datasets/rag_pipeline/datasource_auth.py | 61 ++------- .../datasource_content_preview.py | 9 +- .../datasets/rag_pipeline/rag_pipeline.py | 24 +--- api/controllers/console/explore/audio.py | 10 ++ api/controllers/console/explore/completion.py | 18 +++ .../console/explore/conversation.py | 22 ++++ api/controllers/console/explore/message.py | 18 +++ api/controllers/console/explore/workflow.py | 4 + api/controllers/console/files.py | 5 + api/controllers/console/remote_files.py | 4 + api/controllers/console/spec.py | 7 +- 24 files changed, 182 insertions(+), 303 deletions(-) diff --git a/api/controllers/console/__init__.py b/api/controllers/console/__init__.py index ee02ff3937..621f5066e4 100644 --- a/api/controllers/console/__init__.py +++ b/api/controllers/console/__init__.py @@ -1,31 +1,10 @@ +from importlib import import_module + from flask import Blueprint from flask_restx import Namespace from libs.external_api import ExternalApi -from .app.app_import import AppImportApi, AppImportCheckDependenciesApi, AppImportConfirmApi -from .explore.audio import ChatAudioApi, ChatTextApi -from .explore.completion import ChatApi, ChatStopApi, CompletionApi, CompletionStopApi -from .explore.conversation import ( - ConversationApi, - ConversationListApi, - ConversationPinApi, - ConversationRenameApi, - ConversationUnPinApi, -) -from .explore.message import ( - MessageFeedbackApi, - MessageListApi, - MessageMoreLikeThisApi, - MessageSuggestedQuestionApi, -) -from .explore.workflow import ( - InstalledAppWorkflowRunApi, - InstalledAppWorkflowTaskStopApi, -) -from .files import FileApi, FilePreviewApi, FileSupportTypeApi -from .remote_files import RemoteFileInfoApi, RemoteFileUploadApi - bp = Blueprint("console", __name__, url_prefix="/console/api") api = ExternalApi( @@ -35,23 +14,23 @@ api = ExternalApi( description="Console management APIs for app configuration, monitoring, and administration", ) -# Create namespace console_ns = Namespace("console", description="Console management API operations", path="/") -# File -api.add_resource(FileApi, "/files/upload") -api.add_resource(FilePreviewApi, "/files//preview") -api.add_resource(FileSupportTypeApi, "/files/support-type") +RESOURCE_MODULES = ( + "controllers.console.app.app_import", + "controllers.console.explore.audio", + "controllers.console.explore.completion", + "controllers.console.explore.conversation", + "controllers.console.explore.message", + "controllers.console.explore.workflow", + "controllers.console.files", + "controllers.console.remote_files", +) -# Remote files -api.add_resource(RemoteFileInfoApi, "/remote-files/") -api.add_resource(RemoteFileUploadApi, "/remote-files/upload") - -# Import App -api.add_resource(AppImportApi, "/apps/imports") -api.add_resource(AppImportConfirmApi, "/apps/imports//confirm") -api.add_resource(AppImportCheckDependenciesApi, "/apps/imports//check-dependencies") +for module_name in RESOURCE_MODULES: + import_module(module_name) +# Ensure resource modules are imported so route decorators are evaluated. # Import other controllers from . import ( admin, @@ -150,77 +129,6 @@ from .workspace import ( workspace, ) -# Explore Audio -api.add_resource(ChatAudioApi, "/installed-apps//audio-to-text", endpoint="installed_app_audio") -api.add_resource(ChatTextApi, "/installed-apps//text-to-audio", endpoint="installed_app_text") - -# Explore Completion -api.add_resource( - CompletionApi, "/installed-apps//completion-messages", endpoint="installed_app_completion" -) -api.add_resource( - CompletionStopApi, - "/installed-apps//completion-messages//stop", - endpoint="installed_app_stop_completion", -) -api.add_resource( - ChatApi, "/installed-apps//chat-messages", endpoint="installed_app_chat_completion" -) -api.add_resource( - ChatStopApi, - "/installed-apps//chat-messages//stop", - endpoint="installed_app_stop_chat_completion", -) - -# Explore Conversation -api.add_resource( - ConversationRenameApi, - "/installed-apps//conversations//name", - endpoint="installed_app_conversation_rename", -) -api.add_resource( - ConversationListApi, "/installed-apps//conversations", endpoint="installed_app_conversations" -) -api.add_resource( - ConversationApi, - "/installed-apps//conversations/", - endpoint="installed_app_conversation", -) -api.add_resource( - ConversationPinApi, - "/installed-apps//conversations//pin", - endpoint="installed_app_conversation_pin", -) -api.add_resource( - ConversationUnPinApi, - "/installed-apps//conversations//unpin", - endpoint="installed_app_conversation_unpin", -) - - -# Explore Message -api.add_resource(MessageListApi, "/installed-apps//messages", endpoint="installed_app_messages") -api.add_resource( - MessageFeedbackApi, - "/installed-apps//messages//feedbacks", - endpoint="installed_app_message_feedback", -) -api.add_resource( - MessageMoreLikeThisApi, - "/installed-apps//messages//more-like-this", - endpoint="installed_app_more_like_this", -) -api.add_resource( - MessageSuggestedQuestionApi, - "/installed-apps//messages//suggested-questions", - endpoint="installed_app_suggested_question", -) -# Explore Workflow -api.add_resource(InstalledAppWorkflowRunApi, "/installed-apps//workflows/run") -api.add_resource( - InstalledAppWorkflowTaskStopApi, "/installed-apps//workflows/tasks//stop" -) - api.add_namespace(console_ns) __all__ = [ diff --git a/api/controllers/console/app/app_import.py b/api/controllers/console/app/app_import.py index aee93a8814..c14f597c25 100644 --- a/api/controllers/console/app/app_import.py +++ b/api/controllers/console/app/app_import.py @@ -20,7 +20,10 @@ from services.app_dsl_service import AppDslService, ImportStatus from services.enterprise.enterprise_service import EnterpriseService from services.feature_service import FeatureService +from .. import console_ns + +@console_ns.route("/apps/imports") class AppImportApi(Resource): @setup_required @login_required @@ -74,6 +77,7 @@ class AppImportApi(Resource): return result.model_dump(mode="json"), 200 +@console_ns.route("/apps/imports//confirm") class AppImportConfirmApi(Resource): @setup_required @login_required @@ -98,6 +102,7 @@ class AppImportConfirmApi(Resource): return result.model_dump(mode="json"), 200 +@console_ns.route("/apps/imports//check-dependencies") class AppImportCheckDependenciesApi(Resource): @setup_required @login_required diff --git a/api/controllers/console/auth/data_source_bearer_auth.py b/api/controllers/console/auth/data_source_bearer_auth.py index 796e6916cc..207303b212 100644 --- a/api/controllers/console/auth/data_source_bearer_auth.py +++ b/api/controllers/console/auth/data_source_bearer_auth.py @@ -2,7 +2,7 @@ from flask_login import current_user from flask_restx import Resource, reqparse from werkzeug.exceptions import Forbidden -from controllers.console import api +from controllers.console import console_ns from controllers.console.auth.error import ApiKeyAuthFailedError from libs.login import login_required from services.auth.api_key_auth_service import ApiKeyAuthService @@ -10,6 +10,7 @@ from services.auth.api_key_auth_service import ApiKeyAuthService from ..wraps import account_initialization_required, setup_required +@console_ns.route("/api-key-auth/data-source") class ApiKeyAuthDataSource(Resource): @setup_required @login_required @@ -33,6 +34,7 @@ class ApiKeyAuthDataSource(Resource): return {"sources": []} +@console_ns.route("/api-key-auth/data-source/binding") class ApiKeyAuthDataSourceBinding(Resource): @setup_required @login_required @@ -54,6 +56,7 @@ class ApiKeyAuthDataSourceBinding(Resource): return {"result": "success"}, 200 +@console_ns.route("/api-key-auth/data-source/") class ApiKeyAuthDataSourceBindingDelete(Resource): @setup_required @login_required @@ -66,8 +69,3 @@ class ApiKeyAuthDataSourceBindingDelete(Resource): ApiKeyAuthService.delete_provider_auth(current_user.current_tenant_id, binding_id) return {"result": "success"}, 204 - - -api.add_resource(ApiKeyAuthDataSource, "/api-key-auth/data-source") -api.add_resource(ApiKeyAuthDataSourceBinding, "/api-key-auth/data-source/binding") -api.add_resource(ApiKeyAuthDataSourceBindingDelete, "/api-key-auth/data-source/") diff --git a/api/controllers/console/auth/email_register.py b/api/controllers/console/auth/email_register.py index 91de19a78a..d3613d9183 100644 --- a/api/controllers/console/auth/email_register.py +++ b/api/controllers/console/auth/email_register.py @@ -5,7 +5,7 @@ from sqlalchemy.orm import Session from configs import dify_config from constants.languages import languages -from controllers.console import api +from controllers.console import console_ns from controllers.console.auth.error import ( EmailAlreadyInUseError, EmailCodeError, @@ -25,6 +25,7 @@ from services.billing_service import BillingService from services.errors.account import AccountNotFoundError, AccountRegisterError +@console_ns.route("/email-register/send-email") class EmailRegisterSendEmailApi(Resource): @setup_required @email_password_login_enabled @@ -52,6 +53,7 @@ class EmailRegisterSendEmailApi(Resource): return {"result": "success", "data": token} +@console_ns.route("/email-register/validity") class EmailRegisterCheckApi(Resource): @setup_required @email_password_login_enabled @@ -92,6 +94,7 @@ class EmailRegisterCheckApi(Resource): return {"is_valid": True, "email": token_data.get("email"), "token": new_token} +@console_ns.route("/email-register") class EmailRegisterResetApi(Resource): @setup_required @email_password_login_enabled @@ -148,8 +151,3 @@ class EmailRegisterResetApi(Resource): raise AccountInFreezeError() return account - - -api.add_resource(EmailRegisterSendEmailApi, "/email-register/send-email") -api.add_resource(EmailRegisterCheckApi, "/email-register/validity") -api.add_resource(EmailRegisterResetApi, "/email-register") diff --git a/api/controllers/console/auth/forgot_password.py b/api/controllers/console/auth/forgot_password.py index 36ccb1d562..704bcf8fb8 100644 --- a/api/controllers/console/auth/forgot_password.py +++ b/api/controllers/console/auth/forgot_password.py @@ -221,8 +221,3 @@ class ForgotPasswordResetApi(Resource): TenantService.create_tenant_member(tenant, account, role="owner") account.current_tenant = tenant tenant_was_created.send(tenant) - - -api.add_resource(ForgotPasswordSendEmailApi, "/forgot-password") -api.add_resource(ForgotPasswordCheckApi, "/forgot-password/validity") -api.add_resource(ForgotPasswordResetApi, "/forgot-password/resets") diff --git a/api/controllers/console/auth/login.py b/api/controllers/console/auth/login.py index 3b35ab3c23..ba614aa828 100644 --- a/api/controllers/console/auth/login.py +++ b/api/controllers/console/auth/login.py @@ -7,7 +7,7 @@ from flask_restx import Resource, reqparse import services from configs import dify_config from constants.languages import languages -from controllers.console import api +from controllers.console import console_ns from controllers.console.auth.error import ( AuthenticationFailedError, EmailCodeError, @@ -34,6 +34,7 @@ from services.errors.workspace import WorkSpaceNotAllowedCreateError, Workspaces from services.feature_service import FeatureService +@console_ns.route("/login") class LoginApi(Resource): """Resource for user login.""" @@ -91,6 +92,7 @@ class LoginApi(Resource): return {"result": "success", "data": token_pair.model_dump()} +@console_ns.route("/logout") class LogoutApi(Resource): @setup_required def get(self): @@ -102,6 +104,7 @@ class LogoutApi(Resource): return {"result": "success"} +@console_ns.route("/reset-password") class ResetPasswordSendEmailApi(Resource): @setup_required @email_password_login_enabled @@ -130,6 +133,7 @@ class ResetPasswordSendEmailApi(Resource): return {"result": "success", "data": token} +@console_ns.route("/email-code-login") class EmailCodeLoginSendEmailApi(Resource): @setup_required def post(self): @@ -162,6 +166,7 @@ class EmailCodeLoginSendEmailApi(Resource): return {"result": "success", "data": token} +@console_ns.route("/email-code-login/validity") class EmailCodeLoginApi(Resource): @setup_required def post(self): @@ -218,6 +223,7 @@ class EmailCodeLoginApi(Resource): return {"result": "success", "data": token_pair.model_dump()} +@console_ns.route("/refresh-token") class RefreshTokenApi(Resource): def post(self): parser = reqparse.RequestParser() @@ -229,11 +235,3 @@ class RefreshTokenApi(Resource): return {"result": "success", "data": new_token_pair.model_dump()} except Exception as e: return {"result": "fail", "data": str(e)}, 401 - - -api.add_resource(LoginApi, "/login") -api.add_resource(LogoutApi, "/logout") -api.add_resource(EmailCodeLoginSendEmailApi, "/email-code-login") -api.add_resource(EmailCodeLoginApi, "/email-code-login/validity") -api.add_resource(ResetPasswordSendEmailApi, "/reset-password") -api.add_resource(RefreshTokenApi, "/refresh-token") diff --git a/api/controllers/console/auth/oauth_server.py b/api/controllers/console/auth/oauth_server.py index a54c1443f8..46281860ae 100644 --- a/api/controllers/console/auth/oauth_server.py +++ b/api/controllers/console/auth/oauth_server.py @@ -14,7 +14,7 @@ from models.account import Account from models.model import OAuthProviderApp from services.oauth_server import OAUTH_ACCESS_TOKEN_EXPIRES_IN, OAuthGrantType, OAuthServerService -from .. import api +from .. import console_ns P = ParamSpec("P") R = TypeVar("R") @@ -86,6 +86,7 @@ def oauth_server_access_token_required(view: Callable[Concatenate[T, OAuthProvid return decorated +@console_ns.route("/oauth/provider") class OAuthServerAppApi(Resource): @setup_required @oauth_server_client_id_required @@ -108,6 +109,7 @@ class OAuthServerAppApi(Resource): ) +@console_ns.route("/oauth/provider/authorize") class OAuthServerUserAuthorizeApi(Resource): @setup_required @login_required @@ -125,6 +127,7 @@ class OAuthServerUserAuthorizeApi(Resource): ) +@console_ns.route("/oauth/provider/token") class OAuthServerUserTokenApi(Resource): @setup_required @oauth_server_client_id_required @@ -180,6 +183,7 @@ class OAuthServerUserTokenApi(Resource): ) +@console_ns.route("/oauth/provider/account") class OAuthServerUserAccountApi(Resource): @setup_required @oauth_server_client_id_required @@ -194,9 +198,3 @@ class OAuthServerUserAccountApi(Resource): "timezone": account.timezone, } ) - - -api.add_resource(OAuthServerAppApi, "/oauth/provider") -api.add_resource(OAuthServerUserAuthorizeApi, "/oauth/provider/authorize") -api.add_resource(OAuthServerUserTokenApi, "/oauth/provider/token") -api.add_resource(OAuthServerUserAccountApi, "/oauth/provider/account") diff --git a/api/controllers/console/billing/billing.py b/api/controllers/console/billing/billing.py index 39fc7dec6b..fa89f45122 100644 --- a/api/controllers/console/billing/billing.py +++ b/api/controllers/console/billing/billing.py @@ -1,12 +1,13 @@ from flask_restx import Resource, reqparse -from controllers.console import api +from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, only_edition_cloud, setup_required from libs.login import current_user, login_required from models.model import Account from services.billing_service import BillingService +@console_ns.route("/billing/subscription") class Subscription(Resource): @setup_required @login_required @@ -26,6 +27,7 @@ class Subscription(Resource): ) +@console_ns.route("/billing/invoices") class Invoices(Resource): @setup_required @login_required @@ -36,7 +38,3 @@ class Invoices(Resource): BillingService.is_tenant_owner_or_admin(current_user) assert current_user.current_tenant_id is not None return BillingService.get_invoices(current_user.email, current_user.current_tenant_id) - - -api.add_resource(Subscription, "/billing/subscription") -api.add_resource(Invoices, "/billing/invoices") diff --git a/api/controllers/console/billing/compliance.py b/api/controllers/console/billing/compliance.py index 4bc073f679..e489b48c82 100644 --- a/api/controllers/console/billing/compliance.py +++ b/api/controllers/console/billing/compliance.py @@ -6,10 +6,11 @@ from libs.helper import extract_remote_ip from libs.login import login_required from services.billing_service import BillingService -from .. import api +from .. import console_ns from ..wraps import account_initialization_required, only_edition_cloud, setup_required +@console_ns.route("/compliance/download") class ComplianceApi(Resource): @setup_required @login_required @@ -30,6 +31,3 @@ class ComplianceApi(Resource): ip=ip_address, device_info=device_info, ) - - -api.add_resource(ComplianceApi, "/compliance/download") diff --git a/api/controllers/console/datasets/data_source.py b/api/controllers/console/datasets/data_source.py index 3a9530af84..370e0c0d14 100644 --- a/api/controllers/console/datasets/data_source.py +++ b/api/controllers/console/datasets/data_source.py @@ -9,7 +9,7 @@ from sqlalchemy import select from sqlalchemy.orm import Session from werkzeug.exceptions import NotFound -from controllers.console import api +from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, setup_required from core.datasource.entities.datasource_entities import DatasourceProviderType, OnlineDocumentPagesMessage from core.datasource.online_document.online_document_plugin import OnlineDocumentDatasourcePlugin @@ -27,6 +27,10 @@ from services.datasource_provider_service import DatasourceProviderService from tasks.document_indexing_sync_task import document_indexing_sync_task +@console_ns.route( + "/data-source/integrates", + "/data-source/integrates//", +) class DataSourceApi(Resource): @setup_required @login_required @@ -109,6 +113,7 @@ class DataSourceApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/notion/pre-import/pages") class DataSourceNotionListApi(Resource): @setup_required @login_required @@ -196,6 +201,10 @@ class DataSourceNotionListApi(Resource): return {"notion_info": {**workspace_info, "pages": pages}}, 200 +@console_ns.route( + "/notion/workspaces//pages///preview", + "/datasets/notion-indexing-estimate", +) class DataSourceNotionApi(Resource): @setup_required @login_required @@ -269,6 +278,7 @@ class DataSourceNotionApi(Resource): return response.model_dump(), 200 +@console_ns.route("/datasets//notion/sync") class DataSourceNotionDatasetSyncApi(Resource): @setup_required @login_required @@ -285,6 +295,7 @@ class DataSourceNotionDatasetSyncApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/datasets//documents//notion/sync") class DataSourceNotionDocumentSyncApi(Resource): @setup_required @login_required @@ -301,16 +312,3 @@ class DataSourceNotionDocumentSyncApi(Resource): raise NotFound("Document not found.") document_indexing_sync_task.delay(dataset_id_str, document_id_str) return {"result": "success"}, 200 - - -api.add_resource(DataSourceApi, "/data-source/integrates", "/data-source/integrates//") -api.add_resource(DataSourceNotionListApi, "/notion/pre-import/pages") -api.add_resource( - DataSourceNotionApi, - "/notion/workspaces//pages///preview", - "/datasets/notion-indexing-estimate", -) -api.add_resource(DataSourceNotionDatasetSyncApi, "/datasets//notion/sync") -api.add_resource( - DataSourceNotionDocumentSyncApi, "/datasets//documents//notion/sync" -) diff --git a/api/controllers/console/datasets/datasets_document.py b/api/controllers/console/datasets/datasets_document.py index e6f5daa87b..6aaede0fb3 100644 --- a/api/controllers/console/datasets/datasets_document.py +++ b/api/controllers/console/datasets/datasets_document.py @@ -1114,6 +1114,7 @@ class WebsiteDocumentSyncApi(DocumentResource): return {"result": "success"}, 200 +@console_ns.route("/datasets//documents//pipeline-execution-log") class DocumentPipelineExecutionLogApi(DocumentResource): @setup_required @login_required @@ -1147,29 +1148,3 @@ class DocumentPipelineExecutionLogApi(DocumentResource): "input_data": log.input_data, "datasource_node_id": log.datasource_node_id, }, 200 - - -api.add_resource(GetProcessRuleApi, "/datasets/process-rule") -api.add_resource(DatasetDocumentListApi, "/datasets//documents") -api.add_resource(DatasetInitApi, "/datasets/init") -api.add_resource( - DocumentIndexingEstimateApi, "/datasets//documents//indexing-estimate" -) -api.add_resource(DocumentBatchIndexingEstimateApi, "/datasets//batch//indexing-estimate") -api.add_resource(DocumentBatchIndexingStatusApi, "/datasets//batch//indexing-status") -api.add_resource(DocumentIndexingStatusApi, "/datasets//documents//indexing-status") -api.add_resource(DocumentApi, "/datasets//documents/") -api.add_resource( - DocumentProcessingApi, "/datasets//documents//processing/" -) -api.add_resource(DocumentMetadataApi, "/datasets//documents//metadata") -api.add_resource(DocumentStatusApi, "/datasets//documents/status//batch") -api.add_resource(DocumentPauseApi, "/datasets//documents//processing/pause") -api.add_resource(DocumentRecoverApi, "/datasets//documents//processing/resume") -api.add_resource(DocumentRetryApi, "/datasets//retry") -api.add_resource(DocumentRenameApi, "/datasets//documents//rename") - -api.add_resource(WebsiteDocumentSyncApi, "/datasets//documents//website-sync") -api.add_resource( - DocumentPipelineExecutionLogApi, "/datasets//documents//pipeline-execution-log" -) diff --git a/api/controllers/console/datasets/datasets_segments.py b/api/controllers/console/datasets/datasets_segments.py index 463fd2d7ec..ba552821d2 100644 --- a/api/controllers/console/datasets/datasets_segments.py +++ b/api/controllers/console/datasets/datasets_segments.py @@ -7,7 +7,7 @@ from sqlalchemy import select from werkzeug.exceptions import Forbidden, NotFound import services -from controllers.console import api +from controllers.console import console_ns from controllers.console.app.error import ProviderNotInitializeError from controllers.console.datasets.error import ( ChildChunkDeleteIndexError, @@ -37,6 +37,7 @@ from services.errors.chunk import ChildChunkIndexingError as ChildChunkIndexingS from tasks.batch_create_segment_to_index_task import batch_create_segment_to_index_task +@console_ns.route("/datasets//documents//segments") class DatasetDocumentSegmentListApi(Resource): @setup_required @login_required @@ -139,6 +140,7 @@ class DatasetDocumentSegmentListApi(Resource): return {"result": "success"}, 204 +@console_ns.route("/datasets//documents//segment/") class DatasetDocumentSegmentApi(Resource): @setup_required @login_required @@ -193,6 +195,7 @@ class DatasetDocumentSegmentApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/datasets//documents//segment") class DatasetDocumentSegmentAddApi(Resource): @setup_required @login_required @@ -244,6 +247,7 @@ class DatasetDocumentSegmentAddApi(Resource): return {"data": marshal(segment, segment_fields), "doc_form": document.doc_form}, 200 +@console_ns.route("/datasets//documents//segments/") class DatasetDocumentSegmentUpdateApi(Resource): @setup_required @login_required @@ -345,6 +349,10 @@ class DatasetDocumentSegmentUpdateApi(Resource): return {"result": "success"}, 204 +@console_ns.route( + "/datasets//documents//segments/batch_import", + "/datasets/batch_import_status/", +) class DatasetDocumentSegmentBatchImportApi(Resource): @setup_required @login_required @@ -393,7 +401,9 @@ class DatasetDocumentSegmentBatchImportApi(Resource): @setup_required @login_required @account_initialization_required - def get(self, job_id): + def get(self, job_id=None, dataset_id=None, document_id=None): + if job_id is None: + raise NotFound("The job does not exist.") job_id = str(job_id) indexing_cache_key = f"segment_batch_import_{job_id}" cache_result = redis_client.get(indexing_cache_key) @@ -403,6 +413,7 @@ class DatasetDocumentSegmentBatchImportApi(Resource): return {"job_id": job_id, "job_status": cache_result.decode()}, 200 +@console_ns.route("/datasets//documents//segments//child_chunks") class ChildChunkAddApi(Resource): @setup_required @login_required @@ -553,6 +564,9 @@ class ChildChunkAddApi(Resource): return {"data": marshal(child_chunks, child_chunk_fields)}, 200 +@console_ns.route( + "/datasets//documents//segments//child_chunks/" +) class ChildChunkUpdateApi(Resource): @setup_required @login_required @@ -666,27 +680,3 @@ class ChildChunkUpdateApi(Resource): except ChildChunkIndexingServiceError as e: raise ChildChunkIndexingError(str(e)) return {"data": marshal(child_chunk, child_chunk_fields)}, 200 - - -api.add_resource(DatasetDocumentSegmentListApi, "/datasets//documents//segments") -api.add_resource( - DatasetDocumentSegmentApi, "/datasets//documents//segment/" -) -api.add_resource(DatasetDocumentSegmentAddApi, "/datasets//documents//segment") -api.add_resource( - DatasetDocumentSegmentUpdateApi, - "/datasets//documents//segments/", -) -api.add_resource( - DatasetDocumentSegmentBatchImportApi, - "/datasets//documents//segments/batch_import", - "/datasets/batch_import_status/", -) -api.add_resource( - ChildChunkAddApi, - "/datasets//documents//segments//child_chunks", -) -api.add_resource( - ChildChunkUpdateApi, - "/datasets//documents//segments//child_chunks/", -) diff --git a/api/controllers/console/datasets/metadata.py b/api/controllers/console/datasets/metadata.py index 21ab5e4fe1..53dc80eaa5 100644 --- a/api/controllers/console/datasets/metadata.py +++ b/api/controllers/console/datasets/metadata.py @@ -4,7 +4,7 @@ from flask_login import current_user from flask_restx import Resource, marshal_with, reqparse from werkzeug.exceptions import NotFound -from controllers.console import api +from controllers.console import console_ns from controllers.console.wraps import account_initialization_required, enterprise_license_required, setup_required from fields.dataset_fields import dataset_metadata_fields from libs.login import login_required @@ -16,6 +16,7 @@ from services.entities.knowledge_entities.knowledge_entities import ( from services.metadata_service import MetadataService +@console_ns.route("/datasets//metadata") class DatasetMetadataCreateApi(Resource): @setup_required @login_required @@ -50,6 +51,7 @@ class DatasetMetadataCreateApi(Resource): return MetadataService.get_dataset_metadatas(dataset), 200 +@console_ns.route("/datasets//metadata/") class DatasetMetadataApi(Resource): @setup_required @login_required @@ -87,6 +89,7 @@ class DatasetMetadataApi(Resource): return {"result": "success"}, 204 +@console_ns.route("/datasets/metadata/built-in") class DatasetMetadataBuiltInFieldApi(Resource): @setup_required @login_required @@ -97,6 +100,7 @@ class DatasetMetadataBuiltInFieldApi(Resource): return {"fields": built_in_fields}, 200 +@console_ns.route("/datasets//metadata/built-in/") class DatasetMetadataBuiltInFieldActionApi(Resource): @setup_required @login_required @@ -116,6 +120,7 @@ class DatasetMetadataBuiltInFieldActionApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/datasets//documents/metadata") class DocumentMetadataEditApi(Resource): @setup_required @login_required @@ -136,10 +141,3 @@ class DocumentMetadataEditApi(Resource): MetadataService.update_documents_metadata(dataset, metadata_args) return {"result": "success"}, 200 - - -api.add_resource(DatasetMetadataCreateApi, "/datasets//metadata") -api.add_resource(DatasetMetadataApi, "/datasets//metadata/") -api.add_resource(DatasetMetadataBuiltInFieldApi, "/datasets/metadata/built-in") -api.add_resource(DatasetMetadataBuiltInFieldActionApi, "/datasets//metadata/built-in/") -api.add_resource(DocumentMetadataEditApi, "/datasets//documents/metadata") diff --git a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py index 1a845cf326..154d9e646b 100644 --- a/api/controllers/console/datasets/rag_pipeline/datasource_auth.py +++ b/api/controllers/console/datasets/rag_pipeline/datasource_auth.py @@ -5,7 +5,7 @@ from flask_restx import Resource, reqparse from werkzeug.exceptions import Forbidden, NotFound from configs import dify_config -from controllers.console import api +from controllers.console import console_ns from controllers.console.wraps import ( account_initialization_required, setup_required, @@ -19,6 +19,7 @@ from services.datasource_provider_service import DatasourceProviderService from services.plugin.oauth_service import OAuthProxyService +@console_ns.route("/oauth/plugin//datasource/get-authorization-url") class DatasourcePluginOAuthAuthorizationUrl(Resource): @setup_required @login_required @@ -68,6 +69,7 @@ class DatasourcePluginOAuthAuthorizationUrl(Resource): return response +@console_ns.route("/oauth/plugin//datasource/callback") class DatasourceOAuthCallback(Resource): @setup_required def get(self, provider_id: str): @@ -123,6 +125,7 @@ class DatasourceOAuthCallback(Resource): return redirect(f"{dify_config.CONSOLE_WEB_URL}/oauth-callback") +@console_ns.route("/auth/plugin/datasource/") class DatasourceAuth(Resource): @setup_required @login_required @@ -165,6 +168,7 @@ class DatasourceAuth(Resource): return {"result": datasources}, 200 +@console_ns.route("/auth/plugin/datasource//delete") class DatasourceAuthDeleteApi(Resource): @setup_required @login_required @@ -188,6 +192,7 @@ class DatasourceAuthDeleteApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/auth/plugin/datasource//update") class DatasourceAuthUpdateApi(Resource): @setup_required @login_required @@ -213,6 +218,7 @@ class DatasourceAuthUpdateApi(Resource): return {"result": "success"}, 201 +@console_ns.route("/auth/plugin/datasource/list") class DatasourceAuthListApi(Resource): @setup_required @login_required @@ -225,6 +231,7 @@ class DatasourceAuthListApi(Resource): return {"result": jsonable_encoder(datasources)}, 200 +@console_ns.route("/auth/plugin/datasource/default-list") class DatasourceHardCodeAuthListApi(Resource): @setup_required @login_required @@ -237,6 +244,7 @@ class DatasourceHardCodeAuthListApi(Resource): return {"result": jsonable_encoder(datasources)}, 200 +@console_ns.route("/auth/plugin/datasource//custom-client") class DatasourceAuthOauthCustomClient(Resource): @setup_required @login_required @@ -271,6 +279,7 @@ class DatasourceAuthOauthCustomClient(Resource): return {"result": "success"}, 200 +@console_ns.route("/auth/plugin/datasource//default") class DatasourceAuthDefaultApi(Resource): @setup_required @login_required @@ -291,6 +300,7 @@ class DatasourceAuthDefaultApi(Resource): return {"result": "success"}, 200 +@console_ns.route("/auth/plugin/datasource//update-name") class DatasourceUpdateProviderNameApi(Resource): @setup_required @login_required @@ -311,52 +321,3 @@ class DatasourceUpdateProviderNameApi(Resource): credential_id=args["credential_id"], ) return {"result": "success"}, 200 - - -api.add_resource( - DatasourcePluginOAuthAuthorizationUrl, - "/oauth/plugin//datasource/get-authorization-url", -) -api.add_resource( - DatasourceOAuthCallback, - "/oauth/plugin//datasource/callback", -) -api.add_resource( - DatasourceAuth, - "/auth/plugin/datasource/", -) - -api.add_resource( - DatasourceAuthUpdateApi, - "/auth/plugin/datasource//update", -) - -api.add_resource( - DatasourceAuthDeleteApi, - "/auth/plugin/datasource//delete", -) - -api.add_resource( - DatasourceAuthListApi, - "/auth/plugin/datasource/list", -) - -api.add_resource( - DatasourceHardCodeAuthListApi, - "/auth/plugin/datasource/default-list", -) - -api.add_resource( - DatasourceAuthOauthCustomClient, - "/auth/plugin/datasource//custom-client", -) - -api.add_resource( - DatasourceAuthDefaultApi, - "/auth/plugin/datasource//default", -) - -api.add_resource( - DatasourceUpdateProviderNameApi, - "/auth/plugin/datasource//update-name", -) diff --git a/api/controllers/console/datasets/rag_pipeline/datasource_content_preview.py b/api/controllers/console/datasets/rag_pipeline/datasource_content_preview.py index 05fa681a33..6c04cc877a 100644 --- a/api/controllers/console/datasets/rag_pipeline/datasource_content_preview.py +++ b/api/controllers/console/datasets/rag_pipeline/datasource_content_preview.py @@ -4,7 +4,7 @@ from flask_restx import ( # type: ignore ) from werkzeug.exceptions import Forbidden -from controllers.console import api +from controllers.console import console_ns from controllers.console.datasets.wraps import get_rag_pipeline from controllers.console.wraps import account_initialization_required, setup_required from libs.login import current_user, login_required @@ -13,6 +13,7 @@ from models.dataset import Pipeline from services.rag_pipeline.rag_pipeline import RagPipelineService +@console_ns.route("/rag/pipelines//workflows/published/datasource/nodes//preview") class DataSourceContentPreviewApi(Resource): @setup_required @login_required @@ -49,9 +50,3 @@ class DataSourceContentPreviewApi(Resource): credential_id=args.get("credential_id"), ) return preview_content, 200 - - -api.add_resource( - DataSourceContentPreviewApi, - "/rag/pipelines//workflows/published/datasource/nodes//preview", -) diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline.py index f04b0e04c3..6641911243 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline.py @@ -4,7 +4,7 @@ from flask import request from flask_restx import Resource, reqparse from sqlalchemy.orm import Session -from controllers.console import api +from controllers.console import console_ns from controllers.console.wraps import ( account_initialization_required, enterprise_license_required, @@ -32,6 +32,7 @@ def _validate_description_length(description): return description +@console_ns.route("/rag/pipeline/templates") class PipelineTemplateListApi(Resource): @setup_required @login_required @@ -45,6 +46,7 @@ class PipelineTemplateListApi(Resource): return pipeline_templates, 200 +@console_ns.route("/rag/pipeline/templates/") class PipelineTemplateDetailApi(Resource): @setup_required @login_required @@ -57,6 +59,7 @@ class PipelineTemplateDetailApi(Resource): return pipeline_template, 200 +@console_ns.route("/rag/pipeline/customized/templates/") class CustomizedPipelineTemplateApi(Resource): @setup_required @login_required @@ -112,6 +115,7 @@ class CustomizedPipelineTemplateApi(Resource): return {"data": template.yaml_content}, 200 +@console_ns.route("/rag/pipelines//customized/publish") class PublishCustomizedPipelineTemplateApi(Resource): @setup_required @login_required @@ -144,21 +148,3 @@ class PublishCustomizedPipelineTemplateApi(Resource): rag_pipeline_service = RagPipelineService() rag_pipeline_service.publish_customized_pipeline_template(pipeline_id, args) return {"result": "success"} - - -api.add_resource( - PipelineTemplateListApi, - "/rag/pipeline/templates", -) -api.add_resource( - PipelineTemplateDetailApi, - "/rag/pipeline/templates/", -) -api.add_resource( - CustomizedPipelineTemplateApi, - "/rag/pipeline/customized/templates/", -) -api.add_resource( - PublishCustomizedPipelineTemplateApi, - "/rag/pipelines//customized/publish", -) diff --git a/api/controllers/console/explore/audio.py b/api/controllers/console/explore/audio.py index dc275fe18a..7c20fb49d8 100644 --- a/api/controllers/console/explore/audio.py +++ b/api/controllers/console/explore/audio.py @@ -26,9 +26,15 @@ from services.errors.audio import ( UnsupportedAudioTypeServiceError, ) +from .. import console_ns + logger = logging.getLogger(__name__) +@console_ns.route( + "/installed-apps//audio-to-text", + endpoint="installed_app_audio", +) class ChatAudioApi(InstalledAppResource): def post(self, installed_app): app_model = installed_app.app @@ -65,6 +71,10 @@ class ChatAudioApi(InstalledAppResource): raise InternalServerError() +@console_ns.route( + "/installed-apps//text-to-audio", + endpoint="installed_app_text", +) class ChatTextApi(InstalledAppResource): def post(self, installed_app): from flask_restx import reqparse diff --git a/api/controllers/console/explore/completion.py b/api/controllers/console/explore/completion.py index a99708b7cd..1102b815eb 100644 --- a/api/controllers/console/explore/completion.py +++ b/api/controllers/console/explore/completion.py @@ -33,10 +33,16 @@ from models.model import AppMode from services.app_generate_service import AppGenerateService from services.errors.llm import InvokeRateLimitError +from .. import console_ns + logger = logging.getLogger(__name__) # define completion api for user +@console_ns.route( + "/installed-apps//completion-messages", + endpoint="installed_app_completion", +) class CompletionApi(InstalledAppResource): def post(self, installed_app): app_model = installed_app.app @@ -87,6 +93,10 @@ class CompletionApi(InstalledAppResource): raise InternalServerError() +@console_ns.route( + "/installed-apps//completion-messages//stop", + endpoint="installed_app_stop_completion", +) class CompletionStopApi(InstalledAppResource): def post(self, installed_app, task_id): app_model = installed_app.app @@ -100,6 +110,10 @@ class CompletionStopApi(InstalledAppResource): return {"result": "success"}, 200 +@console_ns.route( + "/installed-apps//chat-messages", + endpoint="installed_app_chat_completion", +) class ChatApi(InstalledAppResource): def post(self, installed_app): app_model = installed_app.app @@ -153,6 +167,10 @@ class ChatApi(InstalledAppResource): raise InternalServerError() +@console_ns.route( + "/installed-apps//chat-messages//stop", + endpoint="installed_app_stop_chat_completion", +) class ChatStopApi(InstalledAppResource): def post(self, installed_app, task_id): app_model = installed_app.app diff --git a/api/controllers/console/explore/conversation.py b/api/controllers/console/explore/conversation.py index 1aef9c544d..feabea2524 100644 --- a/api/controllers/console/explore/conversation.py +++ b/api/controllers/console/explore/conversation.py @@ -16,7 +16,13 @@ from services.conversation_service import ConversationService from services.errors.conversation import ConversationNotExistsError, LastConversationNotExistsError from services.web_conversation_service import WebConversationService +from .. import console_ns + +@console_ns.route( + "/installed-apps//conversations", + endpoint="installed_app_conversations", +) class ConversationListApi(InstalledAppResource): @marshal_with(conversation_infinite_scroll_pagination_fields) def get(self, installed_app): @@ -52,6 +58,10 @@ class ConversationListApi(InstalledAppResource): raise NotFound("Last Conversation Not Exists.") +@console_ns.route( + "/installed-apps//conversations/", + endpoint="installed_app_conversation", +) class ConversationApi(InstalledAppResource): def delete(self, installed_app, c_id): app_model = installed_app.app @@ -70,6 +80,10 @@ class ConversationApi(InstalledAppResource): return {"result": "success"}, 204 +@console_ns.route( + "/installed-apps//conversations//name", + endpoint="installed_app_conversation_rename", +) class ConversationRenameApi(InstalledAppResource): @marshal_with(simple_conversation_fields) def post(self, installed_app, c_id): @@ -95,6 +109,10 @@ class ConversationRenameApi(InstalledAppResource): raise NotFound("Conversation Not Exists.") +@console_ns.route( + "/installed-apps//conversations//pin", + endpoint="installed_app_conversation_pin", +) class ConversationPinApi(InstalledAppResource): def patch(self, installed_app, c_id): app_model = installed_app.app @@ -114,6 +132,10 @@ class ConversationPinApi(InstalledAppResource): return {"result": "success"} +@console_ns.route( + "/installed-apps//conversations//unpin", + endpoint="installed_app_conversation_unpin", +) class ConversationUnPinApi(InstalledAppResource): def patch(self, installed_app, c_id): app_model = installed_app.app diff --git a/api/controllers/console/explore/message.py b/api/controllers/console/explore/message.py index c46c1c1f4f..b045e47846 100644 --- a/api/controllers/console/explore/message.py +++ b/api/controllers/console/explore/message.py @@ -36,9 +36,15 @@ from services.errors.message import ( ) from services.message_service import MessageService +from .. import console_ns + logger = logging.getLogger(__name__) +@console_ns.route( + "/installed-apps//messages", + endpoint="installed_app_messages", +) class MessageListApi(InstalledAppResource): @marshal_with(message_infinite_scroll_pagination_fields) def get(self, installed_app): @@ -66,6 +72,10 @@ class MessageListApi(InstalledAppResource): raise NotFound("First Message Not Exists.") +@console_ns.route( + "/installed-apps//messages//feedbacks", + endpoint="installed_app_message_feedback", +) class MessageFeedbackApi(InstalledAppResource): def post(self, installed_app, message_id): app_model = installed_app.app @@ -93,6 +103,10 @@ class MessageFeedbackApi(InstalledAppResource): return {"result": "success"} +@console_ns.route( + "/installed-apps//messages//more-like-this", + endpoint="installed_app_more_like_this", +) class MessageMoreLikeThisApi(InstalledAppResource): def get(self, installed_app, message_id): app_model = installed_app.app @@ -139,6 +153,10 @@ class MessageMoreLikeThisApi(InstalledAppResource): raise InternalServerError() +@console_ns.route( + "/installed-apps//messages//suggested-questions", + endpoint="installed_app_suggested_question", +) class MessageSuggestedQuestionApi(InstalledAppResource): def get(self, installed_app, message_id): app_model = installed_app.app diff --git a/api/controllers/console/explore/workflow.py b/api/controllers/console/explore/workflow.py index 61e0f1b36a..e32f2814eb 100644 --- a/api/controllers/console/explore/workflow.py +++ b/api/controllers/console/explore/workflow.py @@ -27,9 +27,12 @@ from models.model import AppMode, InstalledApp from services.app_generate_service import AppGenerateService from services.errors.llm import InvokeRateLimitError +from .. import console_ns + logger = logging.getLogger(__name__) +@console_ns.route("/installed-apps//workflows/run") class InstalledAppWorkflowRunApi(InstalledAppResource): def post(self, installed_app: InstalledApp): """ @@ -70,6 +73,7 @@ class InstalledAppWorkflowRunApi(InstalledAppResource): raise InternalServerError() +@console_ns.route("/installed-apps//workflows/tasks//stop") class InstalledAppWorkflowTaskStopApi(InstalledAppResource): def post(self, installed_app: InstalledApp, task_id: str): """ diff --git a/api/controllers/console/files.py b/api/controllers/console/files.py index 105f802878..34f186e2f0 100644 --- a/api/controllers/console/files.py +++ b/api/controllers/console/files.py @@ -26,9 +26,12 @@ from libs.login import login_required from models import Account from services.file_service import FileService +from . import console_ns + PREVIEW_WORDS_LIMIT = 3000 +@console_ns.route("/files/upload") class FileApi(Resource): @setup_required @login_required @@ -88,6 +91,7 @@ class FileApi(Resource): return upload_file, 201 +@console_ns.route("/files//preview") class FilePreviewApi(Resource): @setup_required @login_required @@ -98,6 +102,7 @@ class FilePreviewApi(Resource): return {"content": text} +@console_ns.route("/files/support-type") class FileSupportTypeApi(Resource): @setup_required @login_required diff --git a/api/controllers/console/remote_files.py b/api/controllers/console/remote_files.py index dd4f34b9bd..7aaf807fb0 100644 --- a/api/controllers/console/remote_files.py +++ b/api/controllers/console/remote_files.py @@ -19,7 +19,10 @@ from fields.file_fields import file_fields_with_signed_url, remote_file_info_fie from models.account import Account from services.file_service import FileService +from . import console_ns + +@console_ns.route("/remote-files/") class RemoteFileInfoApi(Resource): @marshal_with(remote_file_info_fields) def get(self, url): @@ -35,6 +38,7 @@ class RemoteFileInfoApi(Resource): } +@console_ns.route("/remote-files/upload") class RemoteFileUploadApi(Resource): @marshal_with(file_fields_with_signed_url) def post(self): diff --git a/api/controllers/console/spec.py b/api/controllers/console/spec.py index ca54715fe0..1795e2d172 100644 --- a/api/controllers/console/spec.py +++ b/api/controllers/console/spec.py @@ -2,7 +2,6 @@ import logging from flask_restx import Resource -from controllers.console import api from controllers.console.wraps import ( account_initialization_required, setup_required, @@ -10,9 +9,12 @@ from controllers.console.wraps import ( from core.schemas.schema_manager import SchemaManager from libs.login import login_required +from . import console_ns + logger = logging.getLogger(__name__) +@console_ns.route("/spec/schema-definitions") class SpecSchemaDefinitionsApi(Resource): @setup_required @login_required @@ -30,6 +32,3 @@ class SpecSchemaDefinitionsApi(Resource): logger.exception("Failed to get schema definitions from local registry") # Return empty array as fallback return [], 200 - - -api.add_resource(SpecSchemaDefinitionsApi, "/spec/schema-definitions") From 87c41c88a3f58844bc5e473c580113761f8aa572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Sun, 28 Sep 2025 13:37:28 +0800 Subject: [PATCH 005/123] fix: some display-related issues (#26335) --- .../builtin_tool/providers/code/_assets/icon.svg | 2 +- .../components/panel/input-field/index.tsx | 2 +- web/i18n/zh-Hant/dataset-pipeline.ts | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/api/core/tools/builtin_tool/providers/code/_assets/icon.svg b/api/core/tools/builtin_tool/providers/code/_assets/icon.svg index b986ed9426..154726a081 100644 --- a/api/core/tools/builtin_tool/providers/code/_assets/icon.svg +++ b/api/core/tools/builtin_tool/providers/code/_assets/icon.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/web/app/components/rag-pipeline/components/panel/input-field/index.tsx b/web/app/components/rag-pipeline/components/panel/input-field/index.tsx index eec18bb471..da00433f30 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/input-field/index.tsx @@ -102,7 +102,7 @@ const InputFieldPanel = () => { return (
-
+
{t('datasetPipeline.inputFieldPanel.title')}
{ - const { locale } = useI18N() - const formatTimeFromNow = useCallback((time: number) => { - return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow() - }, [locale]) - - return { formatTimeFromNow } -} diff --git a/web/hooks/use-format-time-from-now.ts b/web/hooks/use-format-time-from-now.ts index 82704252ac..db3be93df2 100644 --- a/web/hooks/use-format-time-from-now.ts +++ b/web/hooks/use-format-time-from-now.ts @@ -2,14 +2,59 @@ import dayjs from 'dayjs' import relativeTime from 'dayjs/plugin/relativeTime' import { useCallback } from 'react' import { useI18N } from '@/context/i18n' +import type { Locale } from '@/i18n-config' +import 'dayjs/locale/de' +import 'dayjs/locale/es' +import 'dayjs/locale/fa' +import 'dayjs/locale/fr' +import 'dayjs/locale/hi' +import 'dayjs/locale/id' +import 'dayjs/locale/it' +import 'dayjs/locale/ja' +import 'dayjs/locale/ko' +import 'dayjs/locale/pl' +import 'dayjs/locale/pt-br' +import 'dayjs/locale/ro' +import 'dayjs/locale/ru' +import 'dayjs/locale/sl' +import 'dayjs/locale/th' +import 'dayjs/locale/tr' +import 'dayjs/locale/uk' +import 'dayjs/locale/vi' import 'dayjs/locale/zh-cn' +import 'dayjs/locale/zh-tw' dayjs.extend(relativeTime) +const localeMap: Record = { + 'en-US': 'en', + 'zh-Hans': 'zh-cn', + 'zh-Hant': 'zh-tw', + 'pt-BR': 'pt-br', + 'es-ES': 'es', + 'fr-FR': 'fr', + 'de-DE': 'de', + 'ja-JP': 'ja', + 'ko-KR': 'ko', + 'ru-RU': 'ru', + 'it-IT': 'it', + 'th-TH': 'th', + 'id-ID': 'id', + 'uk-UA': 'uk', + 'vi-VN': 'vi', + 'ro-RO': 'ro', + 'pl-PL': 'pl', + 'hi-IN': 'hi', + 'tr-TR': 'tr', + 'fa-IR': 'fa', + 'sl-SI': 'sl', +} + export const useFormatTimeFromNow = () => { const { locale } = useI18N() const formatTimeFromNow = useCallback((time: number) => { - return dayjs(time).locale(locale === 'zh-Hans' ? 'zh-cn' : locale).fromNow() + const dayjsLocale = localeMap[locale] ?? 'en' + return dayjs(time).locale(dayjsLocale).fromNow() }, [locale]) return { formatTimeFromNow } From 272102c06da6243901f1acc1fb2b9e90aa48968c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9D=9E=E6=B3=95=E6=93=8D=E4=BD=9C?= Date: Sun, 28 Sep 2025 13:37:51 +0800 Subject: [PATCH 007/123] doc: fix graph engine readme (#26337) --- api/core/workflow/README.md | 6 +++--- api/core/workflow/graph_engine/layers/README.md | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/api/core/workflow/README.md b/api/core/workflow/README.md index bef19ba90b..72f5dbe1e2 100644 --- a/api/core/workflow/README.md +++ b/api/core/workflow/README.md @@ -60,8 +60,8 @@ Extensible middleware for cross-cutting concerns: ```python engine = GraphEngine(graph) -engine.add_layer(DebugLoggingLayer(level="INFO")) -engine.add_layer(ExecutionLimitsLayer(max_nodes=100)) +engine.layer(DebugLoggingLayer(level="INFO")) +engine.layer(ExecutionLimitsLayer(max_nodes=100)) ``` ### Event-Driven Architecture @@ -117,7 +117,7 @@ The codebase enforces strict layering via import-linter: 1. Create class inheriting from `Layer` base 1. Override lifecycle methods: `on_graph_start()`, `on_event()`, `on_graph_end()` -1. Add to engine via `engine.add_layer()` +1. Add to engine via `engine.layer()` ### Debugging Workflow Execution diff --git a/api/core/workflow/graph_engine/layers/README.md b/api/core/workflow/graph_engine/layers/README.md index 8ee35baec0..17845ee1f0 100644 --- a/api/core/workflow/graph_engine/layers/README.md +++ b/api/core/workflow/graph_engine/layers/README.md @@ -30,7 +30,7 @@ debug_layer = DebugLoggingLayer( ) engine = GraphEngine(graph) -engine.add_layer(debug_layer) +engine.layer(debug_layer) engine.run() ``` From beb1448441708b8f03254f886c115a202d02540e Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Sep 2025 13:43:43 +0800 Subject: [PATCH 008/123] [Chore/Refactor] Add missing 'type' attribute on 'button' components (#26249) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: asukaminato0721 <30024051+asukaminato0721@users.noreply.github.com> --- .../document-detail-navigation-fix.test.tsx | 2 +- web/app/components/app-sidebar/app-info.tsx | 2 +- .../app-sidebar/sidebar-animation-issues.spec.tsx | 4 ++-- .../components/app/annotation/header-opts/index.tsx | 8 ++++---- web/app/components/app/create-app-modal/index.tsx | 2 +- web/app/components/apps/app-card.tsx | 12 +++++++----- web/app/components/apps/new-app-card.tsx | 5 +++-- .../components/base/app-icon-picker/ImageInput.tsx | 2 +- web/app/components/base/app-icon-picker/index.tsx | 2 +- web/app/components/base/audio-btn/index.tsx | 2 +- .../components/base/audio-gallery/AudioPlayer.tsx | 2 +- web/app/components/base/chat/chat/content-switch.tsx | 4 ++-- .../base/date-and-time-picker/calendar/item.tsx | 2 +- .../base/date-and-time-picker/date-picker/header.tsx | 6 +++--- .../year-and-month-picker/header.tsx | 2 +- web/app/components/base/mermaid/index.tsx | 2 +- web/app/components/base/pagination/pagination.tsx | 4 ++-- web/app/components/base/select/locale-signin.tsx | 2 +- web/app/components/base/select/locale.tsx | 2 +- web/app/components/base/theme-selector.tsx | 6 +++--- web/app/components/base/toast/index.spec.tsx | 4 ++-- .../components/base/video-gallery/VideoPlayer.tsx | 6 +++--- .../pricing/plans/self-hosted-plan-item/button.tsx | 2 +- .../list/template-card/edit-pipeline-info.tsx | 2 +- web/app/components/datasets/create/website/index.tsx | 6 +++--- .../components/datasets/documents/detail/index.tsx | 2 +- .../datasets/documents/detail/metadata/index.tsx | 2 +- web/app/components/datasets/documents/operations.tsx | 2 +- web/app/components/develop/doc.tsx | 4 ++-- .../visual-editor/schema-node.tsx | 1 + web/app/components/workflow/run/tracing-panel.tsx | 2 +- web/service/demo/index.tsx | 2 +- 32 files changed, 56 insertions(+), 52 deletions(-) diff --git a/web/__tests__/document-detail-navigation-fix.test.tsx b/web/__tests__/document-detail-navigation-fix.test.tsx index 200ed09ea9..a358744998 100644 --- a/web/__tests__/document-detail-navigation-fix.test.tsx +++ b/web/__tests__/document-detail-navigation-fix.test.tsx @@ -54,7 +54,7 @@ const DocumentDetailWithFix = ({ datasetId, documentId }: { datasetId: string; d return (
-
diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx index d22577c9ad..baf52946df 100644 --- a/web/app/components/app-sidebar/app-info.tsx +++ b/web/app/components/app-sidebar/app-info.tsx @@ -260,7 +260,7 @@ const AppInfo = ({ expand, onlyShowDetail = false, openState = false, onDetailEx return (
{!onlyShowDetail && ( - - -
- - - {(app.mode === 'completion' || app.mode === 'chat') && ( <> : !(isGettingUserCanAccessApp || !userCanAccessApp?.result) && ( <> - @@ -300,13 +301,14 @@ const AppCard = ({ app, onRefresh }: AppCardProps) => { { systemFeatures.webapp_auth.enabled && isCurrentWorkspaceEditor && <> - } - + ((e.target as HTMLInputElement).value = '')} diff --git a/web/app/components/base/app-icon-picker/index.tsx b/web/app/components/base/app-icon-picker/index.tsx index a8de07bf6b..3deb6a6c8f 100644 --- a/web/app/components/base/app-icon-picker/index.tsx +++ b/web/app/components/base/app-icon-picker/index.tsx @@ -117,7 +117,7 @@ const AppIconPicker: FC = ({ {!DISABLE_UPLOAD_IMAGE_AS_ICON &&
{tabs.map(tab => ( -
- -
} -
} - - +
) } diff --git a/web/app/components/base/video-gallery/VideoPlayer.tsx b/web/app/components/base/video-gallery/VideoPlayer.tsx index d7c86a1af9..c2fcd6ee8d 100644 --- a/web/app/components/base/video-gallery/VideoPlayer.tsx +++ b/web/app/components/base/video-gallery/VideoPlayer.tsx @@ -234,13 +234,13 @@ const VideoPlayer: React.FC = ({ src }) => {
- {!isSmallSize && ({formatTime(currentTime)} / {formatTime(duration)})}
- {!isSmallSize && ( @@ -264,7 +264,7 @@ const VideoPlayer: React.FC = ({ src }) => {
)} -
diff --git a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx index 5308490d79..ffa4dbcb65 100644 --- a/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx +++ b/web/app/components/billing/pricing/plans/self-hosted-plan-item/button.tsx @@ -31,7 +31,7 @@ const Button = ({ }, [theme]) return ( -
-
- {ENABLE_WEBSITE_JINAREADER && } - {ENABLE_WEBSITE_FIRECRAWL && } - {ENABLE_WEBSITE_WATERCRAWL &&
- +
From a5387b304e5cb74bf803a6811ca8aa0747ef3e9d Mon Sep 17 00:00:00 2001 From: Yongtao Huang Date: Sun, 28 Sep 2025 13:44:14 +0800 Subject: [PATCH 009/123] Fix: use correct maxLength prop for verification code input (#26244) --- web/app/signup/check-code/page.tsx | 2 +- web/app/signup/components/input-mail.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/signup/check-code/page.tsx b/web/app/signup/check-code/page.tsx index 159965908b..540af74872 100644 --- a/web/app/signup/check-code/page.tsx +++ b/web/app/signup/check-code/page.tsx @@ -93,7 +93,7 @@ export default function CheckCode() {
- setVerifyCode(e.target.value)} max-length={6} className='mt-1' placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> + setVerifyCode(e.target.value)} maxLength={6} className='mt-1' placeholder={t('login.checkCode.verificationCodePlaceholder') as string} /> diff --git a/web/app/signup/components/input-mail.tsx b/web/app/signup/components/input-mail.tsx index 4b0b0ec0b1..d2e7bca65b 100644 --- a/web/app/signup/components/input-mail.tsx +++ b/web/app/signup/components/input-mail.tsx @@ -1,5 +1,5 @@ 'use client' -import { noop } from 'lodash' +import { noop } from 'lodash-es' import Input from '@/app/components/base/input' import { useTranslation } from 'react-i18next' import { useContext } from 'use-context-selector' From 66196459d5a9737f65464f257ffa89011fa96c0f Mon Sep 17 00:00:00 2001 From: AkisAya Date: Sun, 28 Sep 2025 13:44:51 +0800 Subject: [PATCH 010/123] fix db connection error in embed_documents() (#26196) --- api/core/rag/embedding/cached_embedding.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/api/core/rag/embedding/cached_embedding.py b/api/core/rag/embedding/cached_embedding.py index 5f94129a0c..c2f17cd148 100644 --- a/api/core/rag/embedding/cached_embedding.py +++ b/api/core/rag/embedding/cached_embedding.py @@ -42,6 +42,10 @@ class CacheEmbedding(Embeddings): text_embeddings[i] = embedding.get_embedding() else: embedding_queue_indices.append(i) + + # release database connection, because embedding may take a long time + db.session.close() + if embedding_queue_indices: embedding_queue_texts = [texts[i] for i in embedding_queue_indices] embedding_queue_embeddings = [] From e686cc9eabc82e1bfa97e7a0998ae67bd8be3099 Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Sun, 28 Sep 2025 17:45:33 +0800 Subject: [PATCH 011/123] refactor: standardize dataset-pipeline i18n terminology consistency (#26353) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- web/i18n/ja-JP/dataset-pipeline.ts | 6 +++--- web/i18n/zh-Hans/dataset-pipeline.ts | 4 ++-- web/i18n/zh-Hant/dataset-pipeline.ts | 24 ++++++++++++------------ 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/web/i18n/ja-JP/dataset-pipeline.ts b/web/i18n/ja-JP/dataset-pipeline.ts index 6450131933..0dddb25356 100644 --- a/web/i18n/ja-JP/dataset-pipeline.ts +++ b/web/i18n/ja-JP/dataset-pipeline.ts @@ -1,7 +1,7 @@ const translation = { creation: { createFromScratch: { - title: '空白の知識パイプライン', + title: '空白のナレッジパイプライン', description: 'データ処理と構造を完全に制御できるカスタムパイプラインをゼロから作成します。', }, backToKnowledge: 'ナレッジベースに戻る', @@ -77,11 +77,11 @@ const translation = { inputFieldPanel: { uniqueInputs: { title: '各入口のユニークな入力', - tooltip: 'ユニークな入力は、選択したデータソースおよびその下流ノードにのみアクセス可能です。他のデータソースを選択する際、ユーザーはこれを記入する必要はありません。最初のステップ(データソース)には、データソース変数で参照される入力フィールドのみが表示されます。他のフィールドは、第二のステップ(ドキュメントの処理)で表示されます。', + tooltip: 'ユニークな入力は選択したデータソースとその下流ノードのみがアクセス可能です。他のデータソースを選択する際、ユーザーはこれを記入する必要がありません。データソース変数で参照される入力フィールドのみが最初のステップ(データソース)に表示され、他のフィールドは第二のステップ(ドキュメント処理)で表示されます。', }, globalInputs: { title: 'すべての入口に対するグローバル入力', - tooltip: 'グローバル入力はすべてのノードで共有されます。ユーザーは任意のデータソースを選択するときにそれらを入力する必要があります。たとえば、区切り文字や最大チャンク長のようなフィールドは、複数のデータソースに一様に適用できます。データソース変数によって参照される入力フィールドのみが最初のステップ(データソース)に表示されます。他のフィールドは2番目のステップ(文書処理)に表示されます。', + tooltip: 'グローバル入力はすべてのノードで共有されます。ユーザーは任意のデータソースを選択する際にこれらを入力する必要があります。区切り文字や最大チャンク長などのフィールドは複数のデータソースに一様に適用できます。データソース変数で参照される入力フィールドのみが最初のステップ(データソース)に表示され、他のフィールドは第二のステップ(ドキュメント処理)に表示されます。', }, preview: { stepOneTitle: 'データソース', diff --git a/web/i18n/zh-Hans/dataset-pipeline.ts b/web/i18n/zh-Hans/dataset-pipeline.ts index 41c538ce20..7fbe8a0532 100644 --- a/web/i18n/zh-Hans/dataset-pipeline.ts +++ b/web/i18n/zh-Hans/dataset-pipeline.ts @@ -87,11 +87,11 @@ const translation = { description: '用户输入字段用于定义和收集知识流水线执行过程中所需的变量,用户可以自定义字段类型,并灵活配置输入,以满足不同数据源或文档处理的需求。', uniqueInputs: { title: '非共享输入', - tooltip: '非共享输入只能被选定的数据源及其下游节点访问。用户在选择其他数据源时不需要填写它。只有数据源变量引用的输入字段才会出现在第一步(数据源)中。所有其他字段将在第二步(Process Documents)中显示。', + tooltip: '非共享输入只能被选定的数据源及其下游节点访问。用户在选择其他数据源时不需要填写它。只有数据源变量引用的输入字段才会出现在第一步(数据源)中。所有其他字段将在第二步(处理文档)中显示。', }, globalInputs: { title: '全局共享输入', - tooltip: '全局共享输入在所有节点之间共享。用户在选择任何数据源时都需要填写它们。例如,像分隔符(delimiter)和最大块长度(Maximum Chunk Length)这样的字段可以跨多个数据源统一应用。只有数据源变量引用的输入字段才会出现在第一步(数据源)中。所有其他字段都显示在第二步(Process Documents)中。', + tooltip: '全局共享输入在所有节点之间共享。用户在选择任何数据源时都需要填写它们。例如,像分隔符和最大块长度这样的字段可以跨多个数据源统一应用。只有数据源变量引用的输入字段才会出现在第一步(数据源)中。所有其他字段都显示在第二步(处理文档)中。', }, addInputField: '添加输入字段', editInputField: '编辑输入字段', diff --git a/web/i18n/zh-Hant/dataset-pipeline.ts b/web/i18n/zh-Hant/dataset-pipeline.ts index 588bf4cc3f..5dc287a7c9 100644 --- a/web/i18n/zh-Hant/dataset-pipeline.ts +++ b/web/i18n/zh-Hant/dataset-pipeline.ts @@ -1,8 +1,8 @@ const translation = { creation: { createFromScratch: { - title: '空白知識管道', - description: '從頭開始建立自訂管道,並完全控制資料處理和結構。', + title: '空白知識流水線', + description: '從頭開始建立自訂流水線,並完全控制資料處理和結構。', }, caution: '小心', backToKnowledge: '返回知識', @@ -18,12 +18,12 @@ const translation = { convert: '轉換', saveAndProcess: '儲存和處理', choose: '選擇', - useTemplate: '使用此知識管道', + useTemplate: '使用此知識流水線', dataSource: '資料來源', editInfo: '編輯資訊', process: '處理', backToDataSource: '返回資料來源', - exportPipeline: '匯出知識流水線', + exportPipeline: '匯出流水線', details: '詳情', preview: '預覽', }, @@ -33,15 +33,15 @@ const translation = { }, publishPipeline: { success: { - message: '知識管道已發布', + message: '知識流水線已發布', }, error: { - message: '無法發佈知識管道', + message: '無法發佈知識流水線', }, }, publishTemplate: { success: { - message: '管道範本已發佈', + message: '流水線範本已發佈', tip: '您可以在建立頁面上使用此範本。', learnMore: '瞭解詳情', }, @@ -134,17 +134,17 @@ const translation = { title: '證實', content: '此動作是永久性的。您將無法恢復到以前的方法。請確認轉換。', }, - title: '轉換為知識管道', + title: '轉換為知識流水線', warning: '此動作無法復原。', descriptionChunk2: '— 一種更開放和靈活的方法,可以訪問我們市場中的插件。這會將新的處理方法套用至所有未來的文件。', - successMessage: '已成功將資料集轉換成管線', - errorMessage: '無法將資料集轉換成管線', - descriptionChunk1: '您現在可以轉換現有的知識庫,以使用知識管道進行文件處理', + successMessage: '已成功將資料集轉換成流水線', + errorMessage: '無法將資料集轉換成流水線', + descriptionChunk1: '您現在可以轉換現有的知識庫,以使用知識流水線進行文件處理', }, knowledgeDescription: '知識說明', knowledgeNameAndIconPlaceholder: '請輸入知識庫的名稱', knowledgeDescriptionPlaceholder: '描述此知識庫中的內容。詳細的描述使人工智慧能夠更準確地存取資料集的內容。如果為空,Dify 將使用預設命中策略。(選用)', - pipelineNameAndIcon: '管線名稱 & 圖示', + pipelineNameAndIcon: '流水線名稱 & 圖示', knowledgeNameAndIcon: '知識名稱和圖示', inputField: '輸入欄位', knowledgePermissions: '權限', From 36580221aabdf5cc85442e8e73f635e5a4e3e5d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Sun, 28 Sep 2025 17:46:19 +0800 Subject: [PATCH 012/123] fix workflow variable split judge. (#26355) Signed-off-by: zhanluxianshen Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- api/core/workflow/variable_loader.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/core/workflow/variable_loader.py b/api/core/workflow/variable_loader.py index a35215855e..1b31022495 100644 --- a/api/core/workflow/variable_loader.py +++ b/api/core/workflow/variable_loader.py @@ -66,8 +66,8 @@ def load_into_variable_pool( # NOTE(QuantumGhost): this logic needs to be in sync with # `WorkflowEntry.mapping_user_inputs_to_variable_pool`. node_variable_list = key.split(".") - if len(node_variable_list) < 1: - raise ValueError(f"Invalid variable key: {key}. It should have at least one element.") + if len(node_variable_list) < 2: + raise ValueError(f"Invalid variable key: {key}. It should have at least two elements.") if key in user_inputs: continue node_variable_key = ".".join(node_variable_list[1:]) From d00a72a4352a164c02ec3a4ca412c3899670356e Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 28 Sep 2025 17:46:39 +0800 Subject: [PATCH 013/123] fix(graph_engine): block response nodes during streaming (#26364) --- .../workflow/graph_engine/response_coordinator/coordinator.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/api/core/workflow/graph_engine/response_coordinator/coordinator.py b/api/core/workflow/graph_engine/response_coordinator/coordinator.py index 985992f3f1..3db40c545e 100644 --- a/api/core/workflow/graph_engine/response_coordinator/coordinator.py +++ b/api/core/workflow/graph_engine/response_coordinator/coordinator.py @@ -212,10 +212,11 @@ class ResponseStreamCoordinator: edge = self._graph.edges[edge_id] source_node = self._graph.nodes[edge.tail] - # Check if node is a branch/container (original behavior) + # Check if node is a branch, container, or response node if source_node.execution_type in { NodeExecutionType.BRANCH, NodeExecutionType.CONTAINER, + NodeExecutionType.RESPONSE, } or source_node.blocks_variable_output(variable_selectors): blocking_edges.append(edge_id) From 2e914808eaf78919d9e5d67b76b84ede68ba3e64 Mon Sep 17 00:00:00 2001 From: -LAN- Date: Sun, 28 Sep 2025 22:19:11 +0800 Subject: [PATCH 014/123] test(graph_engine): block response nodes during streaming (#26377) Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- .../fixtures/workflow/test-answer-order.yml | 222 ++++++++++++++++++ .../test_answer_order_workflow.py | 28 +++ 2 files changed, 250 insertions(+) create mode 100644 api/tests/fixtures/workflow/test-answer-order.yml create mode 100644 api/tests/unit_tests/core/workflow/graph_engine/test_answer_order_workflow.py diff --git a/api/tests/fixtures/workflow/test-answer-order.yml b/api/tests/fixtures/workflow/test-answer-order.yml new file mode 100644 index 0000000000..3c6631aebb --- /dev/null +++ b/api/tests/fixtures/workflow/test-answer-order.yml @@ -0,0 +1,222 @@ +app: + description: 'this is a chatflow with 2 answer nodes. + + + it''s outouts should like: + + + ``` + + --- answer 1 --- + + + foo + + --- answer 2 --- + + + + + ```' + icon: 🤖 + icon_background: '#FFEAD5' + mode: advanced-chat + name: test-answer-order + use_icon_as_answer_icon: false +dependencies: +- current_identifier: null + type: marketplace + value: + marketplace_plugin_unique_identifier: langgenius/openai:0.2.6@e2665624a156f52160927bceac9e169bd7e5ae6b936ae82575e14c90af390e6e + version: null +kind: app +version: 0.4.0 +workflow: + conversation_variables: [] + environment_variables: [] + features: + file_upload: + allowed_file_extensions: + - .JPG + - .JPEG + - .PNG + - .GIF + - .WEBP + - .SVG + allowed_file_types: + - image + allowed_file_upload_methods: + - local_file + - remote_url + enabled: false + fileUploadConfig: + audio_file_size_limit: 50 + batch_count_limit: 5 + file_size_limit: 15 + image_file_size_limit: 10 + video_file_size_limit: 100 + workflow_file_upload_limit: 10 + image: + enabled: false + number_limits: 3 + transfer_methods: + - local_file + - remote_url + number_limits: 3 + opening_statement: '' + retriever_resource: + enabled: true + sensitive_word_avoidance: + enabled: false + speech_to_text: + enabled: false + suggested_questions: [] + suggested_questions_after_answer: + enabled: false + text_to_speech: + enabled: false + language: '' + voice: '' + graph: + edges: + - data: + isInIteration: false + isInLoop: false + sourceType: answer + targetType: answer + id: 1759052466526-source-1759052469368-target + source: '1759052466526' + sourceHandle: source + target: '1759052469368' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: start + targetType: llm + id: 1759052439553-source-1759052580454-target + source: '1759052439553' + sourceHandle: source + target: '1759052580454' + targetHandle: target + type: custom + zIndex: 0 + - data: + isInIteration: false + isInLoop: false + sourceType: llm + targetType: answer + id: 1759052580454-source-1759052466526-target + source: '1759052580454' + sourceHandle: source + target: '1759052466526' + targetHandle: target + type: custom + zIndex: 0 + nodes: + - data: + selected: false + title: Start + type: start + variables: [] + height: 52 + id: '1759052439553' + position: + x: 30 + y: 242 + positionAbsolute: + x: 30 + y: 242 + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + answer: '--- answer 1 --- + + + foo + + ' + selected: false + title: Answer + type: answer + variables: [] + height: 100 + id: '1759052466526' + position: + x: 632 + y: 242 + positionAbsolute: + x: 632 + y: 242 + selected: true + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + answer: '--- answer 2 --- + + + {{#1759052580454.text#}} + + ' + selected: false + title: Answer 2 + type: answer + variables: [] + height: 103 + id: '1759052469368' + position: + x: 934 + y: 242 + positionAbsolute: + x: 934 + y: 242 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + - data: + context: + enabled: false + variable_selector: [] + model: + completion_params: + temperature: 0.7 + mode: chat + name: gpt-4o + provider: langgenius/openai/openai + prompt_template: + - id: 5c1d873b-06b2-4dce-939e-672882bbd7c0 + role: system + text: '' + - role: user + text: '{{#sys.query#}}' + selected: false + title: LLM + type: llm + vision: + enabled: false + height: 88 + id: '1759052580454' + position: + x: 332 + y: 242 + positionAbsolute: + x: 332 + y: 242 + selected: false + sourcePosition: right + targetPosition: left + type: custom + width: 242 + viewport: + x: 126.2797574512839 + y: 289.55932160537446 + zoom: 1.0743222672006216 + rag_pipeline_variables: [] diff --git a/api/tests/unit_tests/core/workflow/graph_engine/test_answer_order_workflow.py b/api/tests/unit_tests/core/workflow/graph_engine/test_answer_order_workflow.py new file mode 100644 index 0000000000..6569439b56 --- /dev/null +++ b/api/tests/unit_tests/core/workflow/graph_engine/test_answer_order_workflow.py @@ -0,0 +1,28 @@ +from .test_mock_config import MockConfigBuilder +from .test_table_runner import TableTestRunner, WorkflowTestCase + +LLM_NODE_ID = "1759052580454" + + +def test_answer_nodes_emit_in_order() -> None: + mock_config = ( + MockConfigBuilder() + .with_llm_response("unused default") + .with_node_output(LLM_NODE_ID, {"text": "mocked llm text"}) + .build() + ) + + expected_answer = "--- answer 1 ---\n\nfoo\n--- answer 2 ---\n\nmocked llm text\n" + + case = WorkflowTestCase( + fixture_path="test-answer-order", + query="", + expected_outputs={"answer": expected_answer}, + use_auto_mock=True, + mock_config=mock_config, + ) + + runner = TableTestRunner() + result = runner.run_test_case(case) + + assert result.success, result.error From 8d897153a5a0d0479d11eb5d4e218ebd28a6ee89 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Sun, 28 Sep 2025 23:42:22 +0800 Subject: [PATCH 015/123] [Chore/Refactor] Apply @console_ns.route decorators to RAG pipeline controllers (#26348) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: asukaminato0721 <30024051+asukaminato0721@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: -LAN- --- .../rag_pipeline/rag_pipeline_datasets.py | 8 +- .../rag_pipeline_draft_variable.py | 31 +--- .../rag_pipeline/rag_pipeline_import.py | 25 +--- .../rag_pipeline/rag_pipeline_workflow.py | 139 ++++-------------- 4 files changed, 42 insertions(+), 161 deletions(-) diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py index 34faa4ec85..c741bfbf82 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_datasets.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden import services -from controllers.console import api +from controllers.console import console_ns from controllers.console.datasets.error import DatasetNameDuplicateError from controllers.console.wraps import ( account_initialization_required, @@ -32,6 +32,7 @@ def _validate_description_length(description): return description +@console_ns.route("/rag/pipeline/dataset") class CreateRagPipelineDatasetApi(Resource): @setup_required @login_required @@ -84,6 +85,7 @@ class CreateRagPipelineDatasetApi(Resource): return import_info, 201 +@console_ns.route("/rag/pipeline/empty-dataset") class CreateEmptyRagPipelineDatasetApi(Resource): @setup_required @login_required @@ -108,7 +110,3 @@ class CreateEmptyRagPipelineDatasetApi(Resource): ), ) return marshal(dataset, dataset_detail_fields), 201 - - -api.add_resource(CreateRagPipelineDatasetApi, "/rag/pipeline/dataset") -api.add_resource(CreateEmptyRagPipelineDatasetApi, "/rag/pipeline/empty-dataset") diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py index db07e7729a..38f75402a8 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_draft_variable.py @@ -6,7 +6,7 @@ from flask_restx import Resource, fields, inputs, marshal, marshal_with, reqpars from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden -from controllers.console import api +from controllers.console import console_ns from controllers.console.app.error import ( DraftWorkflowNotExist, ) @@ -111,6 +111,7 @@ def _api_prerequisite(f): return wrapper +@console_ns.route("/rag/pipelines//workflows/draft/variables") class RagPipelineVariableCollectionApi(Resource): @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_WITHOUT_VALUE_FIELDS) @@ -168,6 +169,7 @@ def validate_node_id(node_id: str) -> NoReturn | None: return None +@console_ns.route("/rag/pipelines//workflows/draft/nodes//variables") class RagPipelineNodeVariableCollectionApi(Resource): @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) @@ -190,6 +192,7 @@ class RagPipelineNodeVariableCollectionApi(Resource): return Response("", 204) +@console_ns.route("/rag/pipelines//workflows/draft/variables/") class RagPipelineVariableApi(Resource): _PATCH_NAME_FIELD = "name" _PATCH_VALUE_FIELD = "value" @@ -284,6 +287,7 @@ class RagPipelineVariableApi(Resource): return Response("", 204) +@console_ns.route("/rag/pipelines//workflows/draft/variables//reset") class RagPipelineVariableResetApi(Resource): @_api_prerequisite def put(self, pipeline: Pipeline, variable_id: str): @@ -325,6 +329,7 @@ def _get_variable_list(pipeline: Pipeline, node_id) -> WorkflowDraftVariableList return draft_vars +@console_ns.route("/rag/pipelines//workflows/draft/system-variables") class RagPipelineSystemVariableCollectionApi(Resource): @_api_prerequisite @marshal_with(_WORKFLOW_DRAFT_VARIABLE_LIST_FIELDS) @@ -332,6 +337,7 @@ class RagPipelineSystemVariableCollectionApi(Resource): return _get_variable_list(pipeline, SYSTEM_VARIABLE_NODE_ID) +@console_ns.route("/rag/pipelines//workflows/draft/environment-variables") class RagPipelineEnvironmentVariableCollectionApi(Resource): @_api_prerequisite def get(self, pipeline: Pipeline): @@ -364,26 +370,3 @@ class RagPipelineEnvironmentVariableCollectionApi(Resource): ) return {"items": env_vars_list} - - -api.add_resource( - RagPipelineVariableCollectionApi, - "/rag/pipelines//workflows/draft/variables", -) -api.add_resource( - RagPipelineNodeVariableCollectionApi, - "/rag/pipelines//workflows/draft/nodes//variables", -) -api.add_resource( - RagPipelineVariableApi, "/rag/pipelines//workflows/draft/variables/" -) -api.add_resource( - RagPipelineVariableResetApi, "/rag/pipelines//workflows/draft/variables//reset" -) -api.add_resource( - RagPipelineSystemVariableCollectionApi, "/rag/pipelines//workflows/draft/system-variables" -) -api.add_resource( - RagPipelineEnvironmentVariableCollectionApi, - "/rag/pipelines//workflows/draft/environment-variables", -) diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_import.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_import.py index a447f2848a..e0b918456b 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_import.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_import.py @@ -5,7 +5,7 @@ from flask_restx import Resource, marshal_with, reqparse # type: ignore from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden -from controllers.console import api +from controllers.console import console_ns from controllers.console.datasets.wraps import get_rag_pipeline from controllers.console.wraps import ( account_initialization_required, @@ -20,6 +20,7 @@ from services.app_dsl_service import ImportStatus from services.rag_pipeline.rag_pipeline_dsl_service import RagPipelineDslService +@console_ns.route("/rag/pipelines/imports") class RagPipelineImportApi(Resource): @setup_required @login_required @@ -66,6 +67,7 @@ class RagPipelineImportApi(Resource): return result.model_dump(mode="json"), 200 +@console_ns.route("/rag/pipelines/imports//confirm") class RagPipelineImportConfirmApi(Resource): @setup_required @login_required @@ -90,6 +92,7 @@ class RagPipelineImportConfirmApi(Resource): return result.model_dump(mode="json"), 200 +@console_ns.route("/rag/pipelines/imports//check-dependencies") class RagPipelineImportCheckDependenciesApi(Resource): @setup_required @login_required @@ -107,6 +110,7 @@ class RagPipelineImportCheckDependenciesApi(Resource): return result.model_dump(mode="json"), 200 +@console_ns.route("/rag/pipelines//exports") class RagPipelineExportApi(Resource): @setup_required @login_required @@ -128,22 +132,3 @@ class RagPipelineExportApi(Resource): ) return {"data": result}, 200 - - -# Import Rag Pipeline -api.add_resource( - RagPipelineImportApi, - "/rag/pipelines/imports", -) -api.add_resource( - RagPipelineImportConfirmApi, - "/rag/pipelines/imports//confirm", -) -api.add_resource( - RagPipelineImportCheckDependenciesApi, - "/rag/pipelines/imports//check-dependencies", -) -api.add_resource( - RagPipelineExportApi, - "/rag/pipelines//exports", -) diff --git a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py index 01ddb8a871..a75c121fbe 100644 --- a/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py +++ b/api/controllers/console/datasets/rag_pipeline/rag_pipeline_workflow.py @@ -9,7 +9,7 @@ from sqlalchemy.orm import Session from werkzeug.exceptions import Forbidden, InternalServerError, NotFound import services -from controllers.console import api +from controllers.console import console_ns from controllers.console.app.error import ( ConversationCompletedError, DraftWorkflowNotExist, @@ -50,6 +50,7 @@ from services.rag_pipeline.rag_pipeline_transform_service import RagPipelineTran logger = logging.getLogger(__name__) +@console_ns.route("/rag/pipelines//workflows/draft") class DraftRagPipelineApi(Resource): @setup_required @login_required @@ -147,6 +148,7 @@ class DraftRagPipelineApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/draft/iteration/nodes//run") class RagPipelineDraftRunIterationNodeApi(Resource): @setup_required @login_required @@ -181,6 +183,7 @@ class RagPipelineDraftRunIterationNodeApi(Resource): raise InternalServerError() +@console_ns.route("/rag/pipelines//workflows/draft/loop/nodes//run") class RagPipelineDraftRunLoopNodeApi(Resource): @setup_required @login_required @@ -215,6 +218,7 @@ class RagPipelineDraftRunLoopNodeApi(Resource): raise InternalServerError() +@console_ns.route("/rag/pipelines//workflows/draft/run") class DraftRagPipelineRunApi(Resource): @setup_required @login_required @@ -249,6 +253,7 @@ class DraftRagPipelineRunApi(Resource): raise InvokeRateLimitHttpError(ex.description) +@console_ns.route("/rag/pipelines//workflows/published/run") class PublishedRagPipelineRunApi(Resource): @setup_required @login_required @@ -369,6 +374,7 @@ class PublishedRagPipelineRunApi(Resource): # # return result # +@console_ns.route("/rag/pipelines//workflows/published/datasource/nodes//run") class RagPipelinePublishedDatasourceNodeRunApi(Resource): @setup_required @login_required @@ -411,6 +417,7 @@ class RagPipelinePublishedDatasourceNodeRunApi(Resource): ) +@console_ns.route("/rag/pipelines//workflows/draft/datasource/nodes//run") class RagPipelineDraftDatasourceNodeRunApi(Resource): @setup_required @login_required @@ -453,6 +460,7 @@ class RagPipelineDraftDatasourceNodeRunApi(Resource): ) +@console_ns.route("/rag/pipelines//workflows/draft/nodes//run") class RagPipelineDraftNodeRunApi(Resource): @setup_required @login_required @@ -486,6 +494,7 @@ class RagPipelineDraftNodeRunApi(Resource): return workflow_node_execution +@console_ns.route("/rag/pipelines//workflow-runs/tasks//stop") class RagPipelineTaskStopApi(Resource): @setup_required @login_required @@ -504,6 +513,7 @@ class RagPipelineTaskStopApi(Resource): return {"result": "success"} +@console_ns.route("/rag/pipelines//workflows/publish") class PublishedRagPipelineApi(Resource): @setup_required @login_required @@ -559,6 +569,7 @@ class PublishedRagPipelineApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/default-workflow-block-configs") class DefaultRagPipelineBlockConfigsApi(Resource): @setup_required @login_required @@ -577,6 +588,7 @@ class DefaultRagPipelineBlockConfigsApi(Resource): return rag_pipeline_service.get_default_block_configs() +@console_ns.route("/rag/pipelines//workflows/default-workflow-block-configs/") class DefaultRagPipelineBlockConfigApi(Resource): @setup_required @login_required @@ -608,6 +620,7 @@ class DefaultRagPipelineBlockConfigApi(Resource): return rag_pipeline_service.get_default_block_config(node_type=block_type, filters=filters) +@console_ns.route("/rag/pipelines//workflows") class PublishedAllRagPipelineApi(Resource): @setup_required @login_required @@ -656,6 +669,7 @@ class PublishedAllRagPipelineApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/") class RagPipelineByIdApi(Resource): @setup_required @login_required @@ -713,6 +727,7 @@ class RagPipelineByIdApi(Resource): return workflow +@console_ns.route("/rag/pipelines//workflows/published/processing/parameters") class PublishedRagPipelineSecondStepApi(Resource): @setup_required @login_required @@ -738,6 +753,7 @@ class PublishedRagPipelineSecondStepApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/published/pre-processing/parameters") class PublishedRagPipelineFirstStepApi(Resource): @setup_required @login_required @@ -763,6 +779,7 @@ class PublishedRagPipelineFirstStepApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/draft/pre-processing/parameters") class DraftRagPipelineFirstStepApi(Resource): @setup_required @login_required @@ -788,6 +805,7 @@ class DraftRagPipelineFirstStepApi(Resource): } +@console_ns.route("/rag/pipelines//workflows/draft/processing/parameters") class DraftRagPipelineSecondStepApi(Resource): @setup_required @login_required @@ -814,6 +832,7 @@ class DraftRagPipelineSecondStepApi(Resource): } +@console_ns.route("/rag/pipelines//workflow-runs") class RagPipelineWorkflowRunListApi(Resource): @setup_required @login_required @@ -835,6 +854,7 @@ class RagPipelineWorkflowRunListApi(Resource): return result +@console_ns.route("/rag/pipelines//workflow-runs/") class RagPipelineWorkflowRunDetailApi(Resource): @setup_required @login_required @@ -853,6 +873,7 @@ class RagPipelineWorkflowRunDetailApi(Resource): return workflow_run +@console_ns.route("/rag/pipelines//workflow-runs//node-executions") class RagPipelineWorkflowRunNodeExecutionListApi(Resource): @setup_required @login_required @@ -876,6 +897,7 @@ class RagPipelineWorkflowRunNodeExecutionListApi(Resource): return {"data": node_executions} +@console_ns.route("/rag/pipelines/datasource-plugins") class DatasourceListApi(Resource): @setup_required @login_required @@ -891,6 +913,7 @@ class DatasourceListApi(Resource): return jsonable_encoder(RagPipelineManageService.list_rag_pipeline_datasources(tenant_id)) +@console_ns.route("/rag/pipelines//workflows/draft/nodes//last-run") class RagPipelineWorkflowLastRunApi(Resource): @setup_required @login_required @@ -912,6 +935,7 @@ class RagPipelineWorkflowLastRunApi(Resource): return node_exec +@console_ns.route("/rag/pipelines/transform/datasets/") class RagPipelineTransformApi(Resource): @setup_required @login_required @@ -929,6 +953,7 @@ class RagPipelineTransformApi(Resource): return result +@console_ns.route("/rag/pipelines//workflows/draft/datasource/variables-inspect") class RagPipelineDatasourceVariableApi(Resource): @setup_required @login_required @@ -958,6 +983,7 @@ class RagPipelineDatasourceVariableApi(Resource): return workflow_node_execution +@console_ns.route("/rag/pipelines/recommended-plugins") class RagPipelineRecommendedPluginApi(Resource): @setup_required @login_required @@ -966,114 +992,3 @@ class RagPipelineRecommendedPluginApi(Resource): rag_pipeline_service = RagPipelineService() recommended_plugins = rag_pipeline_service.get_recommended_plugins() return recommended_plugins - - -api.add_resource( - DraftRagPipelineApi, - "/rag/pipelines//workflows/draft", -) -api.add_resource( - DraftRagPipelineRunApi, - "/rag/pipelines//workflows/draft/run", -) -api.add_resource( - PublishedRagPipelineRunApi, - "/rag/pipelines//workflows/published/run", -) -api.add_resource( - RagPipelineTaskStopApi, - "/rag/pipelines//workflow-runs/tasks//stop", -) -api.add_resource( - RagPipelineDraftNodeRunApi, - "/rag/pipelines//workflows/draft/nodes//run", -) -api.add_resource( - RagPipelinePublishedDatasourceNodeRunApi, - "/rag/pipelines//workflows/published/datasource/nodes//run", -) - -api.add_resource( - RagPipelineDraftDatasourceNodeRunApi, - "/rag/pipelines//workflows/draft/datasource/nodes//run", -) - -api.add_resource( - RagPipelineDraftRunIterationNodeApi, - "/rag/pipelines//workflows/draft/iteration/nodes//run", -) - -api.add_resource( - RagPipelineDraftRunLoopNodeApi, - "/rag/pipelines//workflows/draft/loop/nodes//run", -) - -api.add_resource( - PublishedRagPipelineApi, - "/rag/pipelines//workflows/publish", -) -api.add_resource( - PublishedAllRagPipelineApi, - "/rag/pipelines//workflows", -) -api.add_resource( - DefaultRagPipelineBlockConfigsApi, - "/rag/pipelines//workflows/default-workflow-block-configs", -) -api.add_resource( - DefaultRagPipelineBlockConfigApi, - "/rag/pipelines//workflows/default-workflow-block-configs/", -) -api.add_resource( - RagPipelineByIdApi, - "/rag/pipelines//workflows/", -) -api.add_resource( - RagPipelineWorkflowRunListApi, - "/rag/pipelines//workflow-runs", -) -api.add_resource( - RagPipelineWorkflowRunDetailApi, - "/rag/pipelines//workflow-runs/", -) -api.add_resource( - RagPipelineWorkflowRunNodeExecutionListApi, - "/rag/pipelines//workflow-runs//node-executions", -) -api.add_resource( - DatasourceListApi, - "/rag/pipelines/datasource-plugins", -) -api.add_resource( - PublishedRagPipelineSecondStepApi, - "/rag/pipelines//workflows/published/processing/parameters", -) -api.add_resource( - PublishedRagPipelineFirstStepApi, - "/rag/pipelines//workflows/published/pre-processing/parameters", -) -api.add_resource( - DraftRagPipelineSecondStepApi, - "/rag/pipelines//workflows/draft/processing/parameters", -) -api.add_resource( - DraftRagPipelineFirstStepApi, - "/rag/pipelines//workflows/draft/pre-processing/parameters", -) -api.add_resource( - RagPipelineWorkflowLastRunApi, - "/rag/pipelines//workflows/draft/nodes//last-run", -) -api.add_resource( - RagPipelineTransformApi, - "/rag/pipelines/transform/datasets/", -) -api.add_resource( - RagPipelineDatasourceVariableApi, - "/rag/pipelines//workflows/draft/datasource/variables-inspect", -) - -api.add_resource( - RagPipelineRecommendedPluginApi, - "/rag/pipelines/recommended-plugins", -) From 029d5d36acd3b430be38fb17e6ba8fba0aded11e Mon Sep 17 00:00:00 2001 From: quicksand Date: Mon, 29 Sep 2025 11:55:06 +0800 Subject: [PATCH 016/123] Fix: Knowledge Base node crash when retrieval_model is null (#26397) --- web/app/components/workflow/nodes/knowledge-base/node.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/workflow/nodes/knowledge-base/node.tsx b/web/app/components/workflow/nodes/knowledge-base/node.tsx index 32810f4d3a..29de1bce9e 100644 --- a/web/app/components/workflow/nodes/knowledge-base/node.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/node.tsx @@ -28,9 +28,9 @@ const Node: FC> = ({ data }) => {
- {settingsDisplay[data.retrieval_model.search_method as keyof typeof settingsDisplay]} + {settingsDisplay[data.retrieval_model?.search_method as keyof typeof settingsDisplay]}
From cba2b9b2ad433cec06402109c1bc515f71358c3d Mon Sep 17 00:00:00 2001 From: "Junyan Qin (Chin)" Date: Mon, 29 Sep 2025 12:57:30 +0800 Subject: [PATCH 017/123] fix: switch plugin auto upgrade cache to redis (#26356) --- api/README.md | 4 +- api/extensions/ext_celery.py | 1 + api/schedule/check_upgradable_plugin_task.py | 4 +- ...ss_tenant_plugin_autoupgrade_check_task.py | 108 ++++++++++++++---- 4 files changed, 93 insertions(+), 24 deletions(-) diff --git a/api/README.md b/api/README.md index 5ecf92a4f0..e75ea3d354 100644 --- a/api/README.md +++ b/api/README.md @@ -80,10 +80,10 @@ 1. If you need to handle and debug the async tasks (e.g. dataset importing and documents indexing), please start the worker service. ```bash -uv run celery -A app.celery worker -P gevent -c 1 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation +uv run celery -A app.celery worker -P gevent -c 2 --loglevel INFO -Q dataset,generation,mail,ops_trace,app_deletion,plugin,workflow_storage,conversation ``` -Addition, if you want to debug the celery scheduled tasks, you can use the following command in another terminal: +Additionally, if you want to debug the celery scheduled tasks, you can run the following command in another terminal to start the beat service: ```bash uv run celery -A app.celery beat diff --git a/api/extensions/ext_celery.py b/api/extensions/ext_celery.py index 585539e2ce..6d7d81ed87 100644 --- a/api/extensions/ext_celery.py +++ b/api/extensions/ext_celery.py @@ -145,6 +145,7 @@ def init_app(app: DifyApp) -> Celery: } if dify_config.ENABLE_CHECK_UPGRADABLE_PLUGIN_TASK and dify_config.MARKETPLACE_ENABLED: imports.append("schedule.check_upgradable_plugin_task") + imports.append("tasks.process_tenant_plugin_autoupgrade_check_task") beat_schedule["check_upgradable_plugin_task"] = { "task": "schedule.check_upgradable_plugin_task.check_upgradable_plugin_task", "schedule": crontab(minute="*/15"), diff --git a/api/schedule/check_upgradable_plugin_task.py b/api/schedule/check_upgradable_plugin_task.py index a9ad27b059..0712100c01 100644 --- a/api/schedule/check_upgradable_plugin_task.py +++ b/api/schedule/check_upgradable_plugin_task.py @@ -6,7 +6,7 @@ import click import app from extensions.ext_database import db from models.account import TenantPluginAutoUpgradeStrategy -from tasks.process_tenant_plugin_autoupgrade_check_task import process_tenant_plugin_autoupgrade_check_task +from tasks import process_tenant_plugin_autoupgrade_check_task as check_task AUTO_UPGRADE_MINIMAL_CHECKING_INTERVAL = 15 * 60 # 15 minutes MAX_CONCURRENT_CHECK_TASKS = 20 @@ -43,7 +43,7 @@ def check_upgradable_plugin_task(): for i in range(0, total_strategies, MAX_CONCURRENT_CHECK_TASKS): batch_strategies = strategies[i : i + MAX_CONCURRENT_CHECK_TASKS] for strategy in batch_strategies: - process_tenant_plugin_autoupgrade_check_task.delay( + check_task.process_tenant_plugin_autoupgrade_check_task.delay( strategy.tenant_id, strategy.strategy_setting, strategy.upgrade_time_of_day, diff --git a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py index bae8f1c4db..124971e8e2 100644 --- a/api/tasks/process_tenant_plugin_autoupgrade_check_task.py +++ b/api/tasks/process_tenant_plugin_autoupgrade_check_task.py @@ -1,5 +1,5 @@ +import json import operator -import traceback import typing import click @@ -9,38 +9,106 @@ from core.helper import marketplace from core.helper.marketplace import MarketplacePluginDeclaration from core.plugin.entities.plugin import PluginInstallationSource from core.plugin.impl.plugin import PluginInstaller +from extensions.ext_redis import redis_client from models.account import TenantPluginAutoUpgradeStrategy RETRY_TIMES_OF_ONE_PLUGIN_IN_ONE_TENANT = 3 +CACHE_REDIS_KEY_PREFIX = "plugin_autoupgrade_check_task:cached_plugin_manifests:" +CACHE_REDIS_TTL = 60 * 15 # 15 minutes -cached_plugin_manifests: dict[str, typing.Union[MarketplacePluginDeclaration, None]] = {} +def _get_redis_cache_key(plugin_id: str) -> str: + """Generate Redis cache key for plugin manifest.""" + return f"{CACHE_REDIS_KEY_PREFIX}{plugin_id}" + + +def _get_cached_manifest(plugin_id: str) -> typing.Union[MarketplacePluginDeclaration, None, bool]: + """ + Get cached plugin manifest from Redis. + Returns: + - MarketplacePluginDeclaration: if found in cache + - None: if cached as not found (marketplace returned no result) + - False: if not in cache at all + """ + try: + key = _get_redis_cache_key(plugin_id) + cached_data = redis_client.get(key) + if cached_data is None: + return False + + cached_json = json.loads(cached_data) + if cached_json is None: + return None + + return MarketplacePluginDeclaration.model_validate(cached_json) + except Exception: + return False + + +def _set_cached_manifest(plugin_id: str, manifest: typing.Union[MarketplacePluginDeclaration, None]) -> None: + """ + Cache plugin manifest in Redis. + Args: + plugin_id: The plugin ID + manifest: The manifest to cache, or None if not found in marketplace + """ + try: + key = _get_redis_cache_key(plugin_id) + if manifest is None: + # Cache the fact that this plugin was not found + redis_client.setex(key, CACHE_REDIS_TTL, json.dumps(None)) + else: + # Cache the manifest data + redis_client.setex(key, CACHE_REDIS_TTL, manifest.model_dump_json()) + except Exception: + # If Redis fails, continue without caching + # traceback.print_exc() + pass def marketplace_batch_fetch_plugin_manifests( plugin_ids_plain_list: list[str], ) -> list[MarketplacePluginDeclaration]: - global cached_plugin_manifests - # return marketplace.batch_fetch_plugin_manifests(plugin_ids_plain_list) - not_included_plugin_ids = [ - plugin_id for plugin_id in plugin_ids_plain_list if plugin_id not in cached_plugin_manifests - ] - if not_included_plugin_ids: - manifests = marketplace.batch_fetch_plugin_manifests_ignore_deserialization_error(not_included_plugin_ids) + """Fetch plugin manifests with Redis caching support.""" + cached_manifests: dict[str, typing.Union[MarketplacePluginDeclaration, None]] = {} + not_cached_plugin_ids: list[str] = [] + + # Check Redis cache for each plugin + for plugin_id in plugin_ids_plain_list: + cached_result = _get_cached_manifest(plugin_id) + if cached_result is False: + # Not in cache, need to fetch + not_cached_plugin_ids.append(plugin_id) + else: + # Either found manifest or cached as None (not found in marketplace) + # At this point, cached_result is either MarketplacePluginDeclaration or None + if isinstance(cached_result, bool): + # This should never happen due to the if condition above, but for type safety + continue + cached_manifests[plugin_id] = cached_result + + # Fetch uncached plugins from marketplace + if not_cached_plugin_ids: + manifests = marketplace.batch_fetch_plugin_manifests_ignore_deserialization_error(not_cached_plugin_ids) + + # Cache the fetched manifests for manifest in manifests: - cached_plugin_manifests[manifest.plugin_id] = manifest + cached_manifests[manifest.plugin_id] = manifest + _set_cached_manifest(manifest.plugin_id, manifest) - if ( - len(manifests) == 0 - ): # this indicates that the plugin not found in marketplace, should set None in cache to prevent future check - for plugin_id in not_included_plugin_ids: - cached_plugin_manifests[plugin_id] = None + # Cache plugins that were not found in marketplace + fetched_plugin_ids = {manifest.plugin_id for manifest in manifests} + for plugin_id in not_cached_plugin_ids: + if plugin_id not in fetched_plugin_ids: + cached_manifests[plugin_id] = None + _set_cached_manifest(plugin_id, None) + # Build result list from cached manifests result: list[MarketplacePluginDeclaration] = [] for plugin_id in plugin_ids_plain_list: - final_manifest = cached_plugin_manifests.get(plugin_id) - if final_manifest is not None: - result.append(final_manifest) + cached_manifest: typing.Union[MarketplacePluginDeclaration, None] = cached_manifests.get(plugin_id) + if cached_manifest is not None: + result.append(cached_manifest) return result @@ -157,10 +225,10 @@ def process_tenant_plugin_autoupgrade_check_task( ) except Exception as e: click.echo(click.style(f"Error when upgrading plugin: {e}", fg="red")) - traceback.print_exc() + # traceback.print_exc() break except Exception as e: click.echo(click.style(f"Error when checking upgradable plugin: {e}", fg="red")) - traceback.print_exc() + # traceback.print_exc() return From d2f05511708db20683e059c66f550aebcf3f373b Mon Sep 17 00:00:00 2001 From: KVOJJJin Date: Mon, 29 Sep 2025 13:42:22 +0800 Subject: [PATCH 018/123] Fix plugin detail panel not show when installed plugins more than 100 (#26405) --- web/app/components/tools/provider-list.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/web/app/components/tools/provider-list.tsx b/web/app/components/tools/provider-list.tsx index d267b49c79..08a4aa0b5d 100644 --- a/web/app/components/tools/provider-list.tsx +++ b/web/app/components/tools/provider-list.tsx @@ -17,7 +17,7 @@ import CardMoreInfo from '@/app/components/plugins/card/card-more-info' import PluginDetailPanel from '@/app/components/plugins/plugin-detail-panel' import MCPList from './mcp' import { useAllToolProviders } from '@/service/use-tools' -import { useInstalledPluginList, useInvalidateInstalledPluginList } from '@/service/use-plugins' +import { useCheckInstalled, useInvalidateInstalledPluginList } from '@/service/use-plugins' import { useGlobalPublicStore } from '@/context/global-public-context' import { ToolTypeEnum } from '../workflow/block-selector/types' import { useMarketplace } from './marketplace/hooks' @@ -77,12 +77,14 @@ const ProviderList = () => { const currentProvider = useMemo(() => { return filteredCollectionList.find(collection => collection.id === currentProviderId) }, [currentProviderId, filteredCollectionList]) - const { data: pluginList } = useInstalledPluginList() + const { data: checkedInstalledData } = useCheckInstalled({ + pluginIds: currentProvider?.plugin_id ? [currentProvider.plugin_id] : [], + enabled: !!currentProvider?.plugin_id, + }) const invalidateInstalledPluginList = useInvalidateInstalledPluginList() const currentPluginDetail = useMemo(() => { - const detail = pluginList?.plugins.find(plugin => plugin.plugin_id === currentProvider?.plugin_id) - return detail - }, [currentProvider?.plugin_id, pluginList?.plugins]) + return checkedInstalledData?.plugins?.[0] + }, [checkedInstalledData]) const toolListTailRef = useRef(null) const showMarketplacePanel = useCallback(() => { From 00f3a53f1ca970e4e96a7bd5d93433970231449a Mon Sep 17 00:00:00 2001 From: lyzno1 <92089059+lyzno1@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:47:48 +0800 Subject: [PATCH 019/123] fix(turbopack): fix animation issues with unnamed animations in CSS modules (#26408) --- .../chat/chat/loading-anim/style.module.css | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/web/app/components/base/chat/chat/loading-anim/style.module.css b/web/app/components/base/chat/chat/loading-anim/style.module.css index b1371ec82a..d5a373df6f 100644 --- a/web/app/components/base/chat/chat/loading-anim/style.module.css +++ b/web/app/components/base/chat/chat/loading-anim/style.module.css @@ -1,6 +1,6 @@ .dot-flashing { position: relative; - animation: 1s infinite linear alternate; + animation: dot-flashing 1s infinite linear alternate; animation-delay: 0.5s; } @@ -10,7 +10,7 @@ display: inline-block; position: absolute; top: 0; - animation: 1s infinite linear alternate; + animation: dot-flashing 1s infinite linear alternate; } .dot-flashing::before { @@ -51,15 +51,21 @@ border-radius: 50%; background-color: #667085; color: #667085; - animation-name: dot-flashing; + animation: dot-flashing 1s infinite linear alternate; +} + +.text { + animation-delay: 0.5s; } .text::before { left: -7px; + animation-delay: 0s; } .text::after { left: 7px; + animation-delay: 1s; } .avatar, @@ -70,13 +76,19 @@ border-radius: 50%; background-color: #155EEF; color: #155EEF; - animation-name: dot-flashing-avatar; + animation: dot-flashing-avatar 1s infinite linear alternate; +} + +.avatar { + animation-delay: 0.5s; } .avatar::before { left: -5px; + animation-delay: 0s; } .avatar::after { left: 5px; + animation-delay: 1s; } From 10d51ada59fa45420122831889972558f96afc81 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:48:00 +0800 Subject: [PATCH 020/123] chore(deps): bump oracledb from 3.0.0 to 3.3.0 in /api (#26396) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- api/pyproject.toml | 2 +- api/uv.lock | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 012702edd2..4bf0b1cac6 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -207,7 +207,7 @@ vdb = [ "couchbase~=4.3.0", "elasticsearch==8.14.0", "opensearch-py==2.4.0", - "oracledb==3.0.0", + "oracledb==3.3.0", "pgvecto-rs[sqlalchemy]~=0.2.1", "pgvector==0.2.5", "pymilvus~=2.5.0", diff --git a/api/uv.lock b/api/uv.lock index 7ce71cd215..262ef3b31d 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1646,7 +1646,7 @@ vdb = [ { name = "elasticsearch", specifier = "==8.14.0" }, { name = "mo-vector", specifier = "~=0.1.13" }, { name = "opensearch-py", specifier = "==2.4.0" }, - { name = "oracledb", specifier = "==3.0.0" }, + { name = "oracledb", specifier = "==3.3.0" }, { name = "pgvecto-rs", extras = ["sqlalchemy"], specifier = "~=0.2.1" }, { name = "pgvector", specifier = "==0.2.5" }, { name = "pymilvus", specifier = "~=2.5.0" }, @@ -4079,23 +4079,23 @@ numpy = [ [[package]] name = "oracledb" -version = "3.0.0" +version = "3.3.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cryptography" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bf/39/712f797b75705c21148fa1d98651f63c2e5cc6876e509a0a9e2f5b406572/oracledb-3.0.0.tar.gz", hash = "sha256:64dc86ee5c032febc556798b06e7b000ef6828bb0252084f6addacad3363db85", size = 840431, upload-time = "2025-03-03T19:36:12.223Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/c9/fae18fa5d803712d188486f8e86ad4f4e00316793ca19745d7c11092c360/oracledb-3.3.0.tar.gz", hash = "sha256:e830d3544a1578296bcaa54c6e8c8ae10a58c7db467c528c4b27adbf9c8b4cb0", size = 811776, upload-time = "2025-07-29T22:34:10.489Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fa/bf/d872c4b3fc15cd3261fe0ea72b21d181700c92dbc050160e161654987062/oracledb-3.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:52daa9141c63dfa75c07d445e9bb7f69f43bfb3c5a173ecc48c798fe50288d26", size = 4312963, upload-time = "2025-03-03T19:36:32.576Z" }, - { url = "https://files.pythonhosted.org/packages/b1/ea/01ee29e76a610a53bb34fdc1030f04b7669c3f80b25f661e07850fc6160e/oracledb-3.0.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:af98941789df4c6aaaf4338f5b5f6b7f2c8c3fe6f8d6a9382f177f350868747a", size = 2661536, upload-time = "2025-03-03T19:36:34.904Z" }, - { url = "https://files.pythonhosted.org/packages/3d/8e/ad380e34a46819224423b4773e58c350bc6269643c8969604097ced8c3bc/oracledb-3.0.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9812bb48865aaec35d73af54cd1746679f2a8a13cbd1412ab371aba2e39b3943", size = 2867461, upload-time = "2025-03-03T19:36:36.508Z" }, - { url = "https://files.pythonhosted.org/packages/96/09/ecc4384a27fd6e1e4de824ae9c160e4ad3aaebdaade5b4bdcf56a4d1ff63/oracledb-3.0.0-cp311-cp311-win32.whl", hash = "sha256:6c27fe0de64f2652e949eb05b3baa94df9b981a4a45fa7f8a991e1afb450c8e2", size = 1752046, upload-time = "2025-03-03T19:36:38.313Z" }, - { url = "https://files.pythonhosted.org/packages/62/e8/f34bde24050c6e55eeba46b23b2291f2dd7fd272fa8b322dcbe71be55778/oracledb-3.0.0-cp311-cp311-win_amd64.whl", hash = "sha256:f922709672002f0b40997456f03a95f03e5712a86c61159951c5ce09334325e0", size = 2101210, upload-time = "2025-03-03T19:36:40.669Z" }, - { url = "https://files.pythonhosted.org/packages/6f/fc/24590c3a3d41e58494bd3c3b447a62835138e5f9b243d9f8da0cfb5da8dc/oracledb-3.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:acd0e747227dea01bebe627b07e958bf36588a337539f24db629dc3431d3f7eb", size = 4351993, upload-time = "2025-03-03T19:36:42.577Z" }, - { url = "https://files.pythonhosted.org/packages/b7/b6/1f3b0b7bb94d53e8857d77b2e8dbdf6da091dd7e377523e24b79dac4fd71/oracledb-3.0.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f8b402f77c22af031cd0051aea2472ecd0635c1b452998f511aa08b7350c90a4", size = 2532640, upload-time = "2025-03-03T19:36:45.066Z" }, - { url = "https://files.pythonhosted.org/packages/72/1a/1815f6c086ab49c00921cf155ff5eede5267fb29fcec37cb246339a5ce4d/oracledb-3.0.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:378a27782e9a37918bd07a5a1427a77cb6f777d0a5a8eac9c070d786f50120ef", size = 2765949, upload-time = "2025-03-03T19:36:47.47Z" }, - { url = "https://files.pythonhosted.org/packages/33/8d/208900f8d372909792ee70b2daad3f7361181e55f2217c45ed9dff658b54/oracledb-3.0.0-cp312-cp312-win32.whl", hash = "sha256:54a28c2cb08316a527cd1467740a63771cc1c1164697c932aa834c0967dc4efc", size = 1709373, upload-time = "2025-03-03T19:36:49.67Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5e/c21754f19c896102793c3afec2277e2180aa7d505e4d7fcca24b52d14e4f/oracledb-3.0.0-cp312-cp312-win_amd64.whl", hash = "sha256:8289bad6d103ce42b140e40576cf0c81633e344d56e2d738b539341eacf65624", size = 2056452, upload-time = "2025-03-03T19:36:51.363Z" }, + { url = "https://files.pythonhosted.org/packages/3f/35/95d9a502fdc48ce1ef3a513ebd027488353441e15aa0448619abb3d09d32/oracledb-3.3.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d9adb74f837838e21898d938e3a725cf73099c65f98b0b34d77146b453e945e0", size = 3963945, upload-time = "2025-07-29T22:34:28.633Z" }, + { url = "https://files.pythonhosted.org/packages/16/a7/8f1ef447d995bb51d9fdc36356697afeceb603932f16410c12d52b2df1a4/oracledb-3.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4b063d1007882570f170ebde0f364e78d4a70c8f015735cc900663278b9ceef7", size = 2449385, upload-time = "2025-07-29T22:34:30.592Z" }, + { url = "https://files.pythonhosted.org/packages/b3/fa/6a78480450bc7d256808d0f38ade3385735fb5a90dab662167b4257dcf94/oracledb-3.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:187728f0a2d161676b8c581a9d8f15d9631a8fea1e628f6d0e9fa2f01280cd22", size = 2634943, upload-time = "2025-07-29T22:34:33.142Z" }, + { url = "https://files.pythonhosted.org/packages/5b/90/ea32b569a45fb99fac30b96f1ac0fb38b029eeebb78357bc6db4be9dde41/oracledb-3.3.0-cp311-cp311-win32.whl", hash = "sha256:920f14314f3402c5ab98f2efc5932e0547e9c0a4ca9338641357f73844e3e2b1", size = 1483549, upload-time = "2025-07-29T22:34:35.015Z" }, + { url = "https://files.pythonhosted.org/packages/81/55/ae60f72836eb8531b630299f9ed68df3fe7868c6da16f820a108155a21f9/oracledb-3.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:825edb97976468db1c7e52c78ba38d75ce7e2b71a2e88f8629bcf02be8e68a8a", size = 1834737, upload-time = "2025-07-29T22:34:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/08/a8/f6b7809d70e98e113786d5a6f1294da81c046d2fa901ad656669fc5d7fae/oracledb-3.3.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9d25e37d640872731ac9b73f83cbc5fc4743cd744766bdb250488caf0d7696a8", size = 3943512, upload-time = "2025-07-29T22:34:39.237Z" }, + { url = "https://files.pythonhosted.org/packages/df/b9/8145ad8991f4864d3de4a911d439e5bc6cdbf14af448f3ab1e846a54210c/oracledb-3.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0bf7cdc2b668f939aa364f552861bc7a149d7cd3f3794730d43ef07613b2bf9", size = 2276258, upload-time = "2025-07-29T22:34:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/56/bf/f65635ad5df17d6e4a2083182750bb136ac663ff0e9996ce59d77d200f60/oracledb-3.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fe20540fde64a6987046807ea47af93be918fd70b9766b3eb803c01e6d4202e", size = 2458811, upload-time = "2025-07-29T22:34:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/7d/30/e0c130b6278c10b0e6cd77a3a1a29a785c083c549676cf701c5d180b8e63/oracledb-3.3.0-cp312-cp312-win32.whl", hash = "sha256:db080be9345cbf9506ffdaea3c13d5314605355e76d186ec4edfa49960ffb813", size = 1445525, upload-time = "2025-07-29T22:34:46.603Z" }, + { url = "https://files.pythonhosted.org/packages/1a/5c/7254f5e1a33a5d6b8bf6813d4f4fdcf5c4166ec8a7af932d987879d5595c/oracledb-3.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:be81e3afe79f6c8ece79a86d6067ad1572d2992ce1c590a086f3755a09535eb4", size = 1789976, upload-time = "2025-07-29T22:34:48.5Z" }, ] [[package]] From c8c94ef87036100597ff848aa89f9603b2e29f2c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:48:24 +0800 Subject: [PATCH 021/123] chore(deps): bump cos-python-sdk-v5 from 1.9.30 to 1.9.38 in /api (#26395) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- api/pyproject.toml | 2 +- api/uv.lock | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/api/pyproject.toml b/api/pyproject.toml index 4bf0b1cac6..485fb918ed 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -180,7 +180,7 @@ dev = [ storage = [ "azure-storage-blob==12.13.0", "bce-python-sdk~=0.9.23", - "cos-python-sdk-v5==1.9.30", + "cos-python-sdk-v5==1.9.38", "esdk-obs-python==3.24.6.1", "google-cloud-storage==2.16.0", "opendal~=0.46.0", diff --git a/api/uv.lock b/api/uv.lock index 262ef3b31d..87799977ca 100644 --- a/api/uv.lock +++ b/api/uv.lock @@ -1076,7 +1076,7 @@ wheels = [ [[package]] name = "cos-python-sdk-v5" -version = "1.9.30" +version = "1.9.38" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "crcmod" }, @@ -1085,7 +1085,10 @@ dependencies = [ { name = "six" }, { name = "xmltodict" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/c4/f2/be99b41433b33a76896680920fca621f191875ca410a66778015e47a501b/cos-python-sdk-v5-1.9.30.tar.gz", hash = "sha256:a23fd090211bf90883066d90cd74317860aa67c6d3aa80fe5e44b18c7e9b2a81", size = 108384, upload-time = "2024-06-14T08:02:37.063Z" } +sdist = { url = "https://files.pythonhosted.org/packages/24/3c/d208266fec7cc3221b449e236b87c3fc1999d5ac4379d4578480321cfecc/cos_python_sdk_v5-1.9.38.tar.gz", hash = "sha256:491a8689ae2f1a6f04dacba66a877b2c8d361456f9cfd788ed42170a1cbf7a9f", size = 98092, upload-time = "2025-07-22T07:56:20.34Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/c8/c9c156aa3bc7caba9b4f8a2b6abec3da6263215988f3fec0ea843f137a10/cos_python_sdk_v5-1.9.38-py3-none-any.whl", hash = "sha256:1d3dd3be2bd992b2e9c2dcd018e2596aa38eab022dbc86b4a5d14c8fc88370e6", size = 92601, upload-time = "2025-08-17T05:12:30.867Z" }, +] [[package]] name = "couchbase" @@ -1624,7 +1627,7 @@ dev = [ storage = [ { name = "azure-storage-blob", specifier = "==12.13.0" }, { name = "bce-python-sdk", specifier = "~=0.9.23" }, - { name = "cos-python-sdk-v5", specifier = "==1.9.30" }, + { name = "cos-python-sdk-v5", specifier = "==1.9.38" }, { name = "esdk-obs-python", specifier = "==3.24.6.1" }, { name = "google-cloud-storage", specifier = "==2.16.0" }, { name = "opendal", specifier = "~=0.46.0" }, From 756864c85b7bdd80b3a1d35c4e0d7e2cc39ac7c6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:48:31 +0800 Subject: [PATCH 022/123] chore(deps-dev): bump @testing-library/jest-dom from 6.6.3 to 6.8.0 in /web (#26393) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package.json | 2 +- web/pnpm-lock.yaml | 31 ++++++++++++++++++------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/web/package.json b/web/package.json index cf49429659..269ff8cba4 100644 --- a/web/package.json +++ b/web/package.json @@ -177,7 +177,7 @@ "@storybook/react": "8.5.0", "@storybook/test": "8.5.0", "@testing-library/dom": "^10.4.0", - "@testing-library/jest-dom": "^6.6.2", + "@testing-library/jest-dom": "^6.8.0", "@testing-library/react": "^16.0.1", "@types/crypto-js": "^4.2.2", "@types/dagre": "^0.7.52", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index e47985fd71..38f120c126 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -450,8 +450,8 @@ importers: specifier: ^10.4.0 version: 10.4.0 '@testing-library/jest-dom': - specifier: ^6.6.2 - version: 6.6.3 + specifier: ^6.8.0 + version: 6.8.0 '@testing-library/react': specifier: ^16.0.1 version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1) @@ -593,8 +593,8 @@ importers: packages: - '@adobe/css-tools@4.4.3': - resolution: {integrity: sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==} + '@adobe/css-tools@4.4.4': + resolution: {integrity: sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==} '@alloc/quick-lru@5.2.0': resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} @@ -3208,8 +3208,8 @@ packages: resolution: {integrity: sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} - '@testing-library/jest-dom@6.6.3': - resolution: {integrity: sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==} + '@testing-library/jest-dom@6.8.0': + resolution: {integrity: sha512-WgXcWzVM6idy5JaftTVC8Vs83NKRmGJz4Hqs4oyOuO2J4r/y79vvKZsb+CaGyCSEbUPI6OsewfPd0G1A0/TUZQ==} engines: {node: '>=14', npm: '>=6', yarn: '>=1'} '@testing-library/react@16.3.0': @@ -8216,6 +8216,10 @@ packages: resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} engines: {node: '>=12'} + strip-indent@4.1.0: + resolution: {integrity: sha512-OA95x+JPmL7kc7zCu+e+TeYxEiaIyndRx0OrBcK2QPPH09oAndr2ALvymxWA+Lx1PYYvFUm4O63pRkdJAaW96w==} + engines: {node: '>=12'} + strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} @@ -9006,7 +9010,7 @@ packages: snapshots: - '@adobe/css-tools@4.4.3': {} + '@adobe/css-tools@4.4.4': {} '@alloc/quick-lru@5.2.0': {} @@ -12175,7 +12179,7 @@ snapshots: '@testing-library/jest-dom@6.5.0': dependencies: - '@adobe/css-tools': 4.4.3 + '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 chalk: 3.0.0 css.escape: 1.5.1 @@ -12183,14 +12187,13 @@ snapshots: lodash: 4.17.21 redent: 3.0.0 - '@testing-library/jest-dom@6.6.3': + '@testing-library/jest-dom@6.8.0': dependencies: - '@adobe/css-tools': 4.4.3 + '@adobe/css-tools': 4.4.4 aria-query: 5.3.2 - chalk: 3.0.0 css.escape: 1.5.1 dom-accessibility-api: 0.6.3 - lodash: 4.17.21 + picocolors: 1.1.1 redent: 3.0.0 '@testing-library/react@16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.7(@types/react@19.1.11))(@types/react@19.1.11)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)': @@ -17474,7 +17477,7 @@ snapshots: '@types/resolve': 1.20.6 doctrine: 3.0.0 resolve: 1.22.10 - strip-indent: 4.0.0 + strip-indent: 4.1.0 transitivePeerDependencies: - supports-color @@ -18313,6 +18316,8 @@ snapshots: dependencies: min-indent: 1.0.1 + strip-indent@4.1.0: {} + strip-json-comments@3.1.1: {} style-loader@3.3.4(webpack@5.100.2(esbuild@0.25.0)(uglify-js@3.19.3)): From 30617feff85fa2a08090fe42eada4171661ffff3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 13:50:36 +0800 Subject: [PATCH 023/123] chore(deps-dev): bump @eslint/js from 9.31.0 to 9.36.0 in /web (#26392) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- web/package.json | 2 +- web/pnpm-lock.yaml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/web/package.json b/web/package.json index 269ff8cba4..f40c346f82 100644 --- a/web/package.json +++ b/web/package.json @@ -158,7 +158,7 @@ "@chromatic-com/storybook": "^3.1.0", "@eslint-react/eslint-plugin": "^1.15.0", "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "^9.20.0", + "@eslint/js": "^9.36.0", "@faker-js/faker": "^9.0.3", "@happy-dom/jest-environment": "^17.4.4", "@mdx-js/loader": "^3.1.0", diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 38f120c126..764202490e 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -393,8 +393,8 @@ importers: specifier: ^3.1.0 version: 3.3.1 '@eslint/js': - specifier: ^9.20.0 - version: 9.31.0 + specifier: ^9.36.0 + version: 9.36.0 '@faker-js/faker': specifier: ^9.0.3 version: 9.9.0 @@ -1660,14 +1660,14 @@ packages: resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.31.0': - resolution: {integrity: sha512-LOm5OVt7D4qiKCqoiPbA7LWmI+tbw1VbTUowBcUMgQSuM6poJufkFkYDcQpo5KfgD39TnNySV26QjOh7VFpSyw==} - engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@eslint/js@9.35.0': resolution: {integrity: sha512-30iXE9whjlILfWobBkNerJo+TXYsgVM5ERQwMcMKCHckHflCmf7wXDAHlARoWnh0s1U72WqlbeyE7iAcCzuCPw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/js@9.36.0': + resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@eslint/markdown@7.1.0': resolution: {integrity: sha512-Y+X1B1j+/zupKDVJfkKc8uYMjQkGzfnd8lt7vK3y8x9Br6H5dBuhAfFrQ6ff7HAMm/1BwgecyEiRFkYCWPRxmA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -10312,10 +10312,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@eslint/js@9.31.0': {} - '@eslint/js@9.35.0': {} + '@eslint/js@9.36.0': {} + '@eslint/markdown@7.1.0': dependencies: '@eslint/core': 0.15.1 From 70bc5ca7f436edb655aba52d558ca71a2e01e758 Mon Sep 17 00:00:00 2001 From: Timo <57227498+EchterTimo@users.noreply.github.com> Date: Mon, 29 Sep 2025 07:50:53 +0200 Subject: [PATCH 024/123] Add missing import "IO" in client.py (#26389) Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --- sdks/python-client/dify_client/client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdks/python-client/dify_client/client.py b/sdks/python-client/dify_client/client.py index 201391eae9..2154741e91 100644 --- a/sdks/python-client/dify_client/client.py +++ b/sdks/python-client/dify_client/client.py @@ -1,5 +1,5 @@ import json -from typing import Literal +from typing import IO, Literal import requests From 595df172a8526afa81de791e7cff4b00bec7089d Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Mon, 29 Sep 2025 13:51:48 +0800 Subject: [PATCH 025/123] fix: model list refresh when change credential (#26421) --- .../header/account-setting/model-provider-page/hooks.ts | 9 ++++++--- .../model-provider-page/model-auth/hooks/use-auth.ts | 6 +++--- .../provider-added-card/model-load-balancing-modal.tsx | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/web/app/components/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts index b10aeeb47e..48dc609795 100644 --- a/web/app/components/header/account-setting/model-provider-page/hooks.ts +++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts @@ -323,15 +323,18 @@ export const useRefreshModel = () => { const { eventEmitter } = useEventEmitterContextContext() const updateModelProviders = useUpdateModelProviders() const updateModelList = useUpdateModelList() - const handleRefreshModel = useCallback((provider: ModelProvider, configurationMethod: ConfigurationMethodEnum, CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields) => { + const handleRefreshModel = useCallback(( + provider: ModelProvider, + CustomConfigurationModelFixedFields?: CustomConfigurationModelFixedFields, + refreshModelList?: boolean, + ) => { updateModelProviders() provider.supported_model_types.forEach((type) => { updateModelList(type) }) - if (configurationMethod === ConfigurationMethodEnum.customizableModel - && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) { + if (refreshModelList && provider.custom_configuration.status === CustomConfigurationStatusEnum.active) { eventEmitter?.emit({ type: UPDATE_MODEL_PROVIDER_CUSTOM_MODEL_LIST, payload: provider.provider, diff --git a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts index 14b21be7f7..3136a70563 100644 --- a/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts +++ b/web/app/components/header/account-setting/model-provider-page/model-auth/hooks/use-auth.ts @@ -90,7 +90,7 @@ export const useAuth = ( type: 'success', message: t('common.api.actionSuccess'), }) - handleRefreshModel(provider, configurationMethod, undefined) + handleRefreshModel(provider, undefined, true) } finally { handleSetDoingAction(false) @@ -125,7 +125,7 @@ export const useAuth = ( type: 'success', message: t('common.api.actionSuccess'), }) - handleRefreshModel(provider, configurationMethod, undefined) + handleRefreshModel(provider, undefined, true) onRemove?.(pendingOperationCredentialId.current ?? '') closeConfirmDelete() } @@ -147,7 +147,7 @@ export const useAuth = ( if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - handleRefreshModel(provider, configurationMethod, undefined) + handleRefreshModel(provider, undefined, !payload.credential_id) } } finally { diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx index 070c2ee90f..090147897b 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-modal.tsx @@ -159,7 +159,7 @@ const ModelLoadBalancingModal = ({ ) if (res.result === 'success') { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) - handleRefreshModel(provider, configurateMethod, currentCustomConfigurationModelFixedFields) + handleRefreshModel(provider, currentCustomConfigurationModelFixedFields, false) onSave?.(provider.provider) onClose?.() } From af662b100bd8b74d23b730c2207806208b980850 Mon Sep 17 00:00:00 2001 From: JoJohanse <97782983+JoJohanse@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:35:05 +0800 Subject: [PATCH 026/123] Fixes#26332 Remove FILES_URL in default .yaml settings (#26410) Co-authored-by: crazywoola <427733928@qq.com> --- docker/.env.example | 2 +- docker/docker-compose.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/.env.example b/docker/.env.example index c0f084796e..8c23bfc9b7 100644 --- a/docker/.env.example +++ b/docker/.env.example @@ -45,7 +45,7 @@ APP_WEB_URL= # Recommendation: use a dedicated domain (e.g., https://upload.example.com). # Alternatively, use http://:5001 or http://api:5001, # ensuring port 5001 is externally accessible (see docker-compose.yaml). -FILES_URL=http://api:5001 +FILES_URL= # INTERNAL_FILES_URL is used for plugin daemon communication within Docker network. # Set this to the internal Docker service URL for proper plugin file access. diff --git a/docker/docker-compose.yaml b/docker/docker-compose.yaml index 2617f84e7d..cc66f69550 100644 --- a/docker/docker-compose.yaml +++ b/docker/docker-compose.yaml @@ -10,7 +10,7 @@ x-shared-env: &shared-api-worker-env SERVICE_API_URL: ${SERVICE_API_URL:-} APP_API_URL: ${APP_API_URL:-} APP_WEB_URL: ${APP_WEB_URL:-} - FILES_URL: ${FILES_URL:-http://api:5001} + FILES_URL: ${FILES_URL:-} INTERNAL_FILES_URL: ${INTERNAL_FILES_URL:-} LANG: ${LANG:-en_US.UTF-8} LC_ALL: ${LC_ALL:-en_US.UTF-8} From 1a7898dff1a69791854a57d773520cb170e18bc6 Mon Sep 17 00:00:00 2001 From: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Date: Mon, 29 Sep 2025 14:58:28 +0800 Subject: [PATCH 027/123] fix: Fix retrieval configuration handling in dataset components (#26361) Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../configuration/dataset-config/index.tsx | 31 +++- .../params-config/config-content.tsx | 27 ++-- .../components/app/configuration/index.tsx | 32 +++-- .../common/retrieval-method-config/index.tsx | 4 +- .../common/retrieval-param-config/index.tsx | 4 +- .../components/retrieval-config.tsx | 100 ++++++------- .../nodes/knowledge-retrieval/default.ts | 4 +- .../nodes/knowledge-retrieval/panel.tsx | 10 +- .../nodes/knowledge-retrieval/use-config.ts | 6 +- .../nodes/knowledge-retrieval/utils.ts | 132 ++++++++++++------ 10 files changed, 216 insertions(+), 134 deletions(-) diff --git a/web/app/components/app/configuration/dataset-config/index.tsx b/web/app/components/app/configuration/dataset-config/index.tsx index 6165cfdeec..65ef74bc27 100644 --- a/web/app/components/app/configuration/dataset-config/index.tsx +++ b/web/app/components/app/configuration/dataset-config/index.tsx @@ -65,13 +65,40 @@ const DatasetConfig: FC = () => { const onRemove = (id: string) => { const filteredDataSets = dataSet.filter(item => item.id !== id) setDataSet(filteredDataSets) - const retrievalConfig = getMultipleRetrievalConfig(datasetConfigs as any, filteredDataSets, dataSet, { + const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = datasetConfigs + const { + top_k, + score_threshold, + reranking_model, + reranking_mode, + weights, + reranking_enable, + } = restConfigs + const oldRetrievalConfig = { + top_k, + score_threshold, + reranking_model: (reranking_model.reranking_provider_name && reranking_model.reranking_model_name) ? { + provider: reranking_model.reranking_provider_name, + model: reranking_model.reranking_model_name, + } : undefined, + reranking_mode, + weights, + reranking_enable, + } + const retrievalConfig = getMultipleRetrievalConfig(oldRetrievalConfig, filteredDataSets, dataSet, { provider: currentRerankProvider?.provider, model: currentRerankModel?.model, }) setDatasetConfigs({ - ...(datasetConfigs as any), + ...datasetConfigsRef.current, ...retrievalConfig, + reranking_model: { + reranking_provider_name: retrievalConfig?.reranking_model?.provider || '', + reranking_model_name: retrievalConfig?.reranking_model?.model || '', + }, + retrieval_model, + score_threshold_enabled, + datasets, }) const { allExternal, diff --git a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx index cb61b927bc..1558d32fc6 100644 --- a/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx +++ b/web/app/components/app/configuration/dataset-config/params-config/config-content.tsx @@ -30,11 +30,11 @@ import { noop } from 'lodash-es' type Props = { datasetConfigs: DatasetConfigs onChange: (configs: DatasetConfigs, isRetrievalModeChange?: boolean) => void + selectedDatasets?: DataSet[] isInWorkflow?: boolean singleRetrievalModelConfig?: ModelConfig onSingleRetrievalModelChange?: (config: ModelConfig) => void onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void - selectedDatasets?: DataSet[] } const ConfigContent: FC = ({ @@ -61,22 +61,28 @@ const ConfigContent: FC = ({ const { modelList: rerankModelList, + currentModel: validDefaultRerankModel, + currentProvider: validDefaultRerankProvider, } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) + /** + * If reranking model is set and is valid, use the reranking model + * Otherwise, check if the default reranking model is valid + */ const { currentModel: currentRerankModel, } = useCurrentProviderAndModel( rerankModelList, { - provider: datasetConfigs.reranking_model?.reranking_provider_name, - model: datasetConfigs.reranking_model?.reranking_model_name, + provider: datasetConfigs.reranking_model?.reranking_provider_name || validDefaultRerankProvider?.provider || '', + model: datasetConfigs.reranking_model?.reranking_model_name || validDefaultRerankModel?.model || '', }, ) const rerankModel = useMemo(() => { return { - provider_name: datasetConfigs?.reranking_model?.reranking_provider_name ?? '', - model_name: datasetConfigs?.reranking_model?.reranking_model_name ?? '', + provider_name: datasetConfigs.reranking_model?.reranking_provider_name ?? '', + model_name: datasetConfigs.reranking_model?.reranking_model_name ?? '', } }, [datasetConfigs.reranking_model]) @@ -135,7 +141,7 @@ const ConfigContent: FC = ({ }) } - const model = singleRetrievalConfig + const model = singleRetrievalConfig // Legacy code, for compatibility, have to keep it const rerankingModeOptions = [ { @@ -158,7 +164,7 @@ const ConfigContent: FC = ({ const canManuallyToggleRerank = useMemo(() => { return (selectedDatasetsMode.allInternal && selectedDatasetsMode.allEconomic) - || selectedDatasetsMode.allExternal + || selectedDatasetsMode.allExternal }, [selectedDatasetsMode.allEconomic, selectedDatasetsMode.allExternal, selectedDatasetsMode.allInternal]) const showRerankModel = useMemo(() => { @@ -168,7 +174,7 @@ const ConfigContent: FC = ({ return datasetConfigs.reranking_enable }, [datasetConfigs.reranking_enable, canManuallyToggleRerank]) - const handleDisabledSwitchClick = useCallback((enable: boolean) => { + const handleManuallyToggleRerank = useCallback((enable: boolean) => { if (!currentRerankModel && enable) Toast.notify({ type: 'error', message: t('workflow.errorMsg.rerankModelRequired') }) onChange({ @@ -255,12 +261,11 @@ const ConfigContent: FC = ({
{ - selectedDatasetsMode.allEconomic && !selectedDatasetsMode.mixtureInternalAndExternal && ( + canManuallyToggleRerank && ( ) } diff --git a/web/app/components/app/configuration/index.tsx b/web/app/components/app/configuration/index.tsx index 091900642a..f1f81ebf97 100644 --- a/web/app/components/app/configuration/index.tsx +++ b/web/app/components/app/configuration/index.tsx @@ -284,18 +284,28 @@ const Configuration: FC = () => { setRerankSettingModalOpen(true) const { datasets, retrieval_model, score_threshold_enabled, ...restConfigs } = datasetConfigs + const { + top_k, + score_threshold, + reranking_model, + reranking_mode, + weights, + reranking_enable, + } = restConfigs - const retrievalConfig = getMultipleRetrievalConfig({ - top_k: restConfigs.top_k, - score_threshold: restConfigs.score_threshold, - reranking_model: restConfigs.reranking_model && { - provider: restConfigs.reranking_model.reranking_provider_name, - model: restConfigs.reranking_model.reranking_model_name, - }, - reranking_mode: restConfigs.reranking_mode, - weights: restConfigs.weights, - reranking_enable: restConfigs.reranking_enable, - }, newDatasets, dataSets, { + const oldRetrievalConfig = { + top_k, + score_threshold, + reranking_model: (reranking_model.reranking_provider_name && reranking_model.reranking_model_name) ? { + provider: reranking_model.reranking_provider_name, + model: reranking_model.reranking_model_name, + } : undefined, + reranking_mode, + weights, + reranking_enable, + } + + const retrievalConfig = getMultipleRetrievalConfig(oldRetrievalConfig, newDatasets, dataSets, { provider: currentRerankProvider?.provider, model: currentRerankModel?.model, }) diff --git a/web/app/components/datasets/common/retrieval-method-config/index.tsx b/web/app/components/datasets/common/retrieval-method-config/index.tsx index 57d357442f..ed230c52ce 100644 --- a/web/app/components/datasets/common/retrieval-method-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-method-config/index.tsx @@ -40,7 +40,7 @@ const RetrievalMethodConfig: FC = ({ onChange({ ...value, search_method: retrieveMethod, - ...(!value.reranking_model.reranking_model_name + ...((!value.reranking_model.reranking_model_name || !value.reranking_model.reranking_provider_name) ? { reranking_model: { reranking_provider_name: isRerankDefaultModelValid ? rerankDefaultModel?.provider?.provider ?? '' : '', @@ -57,7 +57,7 @@ const RetrievalMethodConfig: FC = ({ onChange({ ...value, search_method: retrieveMethod, - ...(!value.reranking_model.reranking_model_name + ...((!value.reranking_model.reranking_model_name || !value.reranking_model.reranking_provider_name) ? { reranking_model: { reranking_provider_name: isRerankDefaultModelValid ? rerankDefaultModel?.provider?.provider ?? '' : '', diff --git a/web/app/components/datasets/common/retrieval-param-config/index.tsx b/web/app/components/datasets/common/retrieval-param-config/index.tsx index 216a56ab16..0c28149d56 100644 --- a/web/app/components/datasets/common/retrieval-param-config/index.tsx +++ b/web/app/components/datasets/common/retrieval-param-config/index.tsx @@ -54,7 +54,7 @@ const RetrievalParamConfig: FC = ({ }, ) - const handleDisabledSwitchClick = useCallback((enable: boolean) => { + const handleToggleRerankEnable = useCallback((enable: boolean) => { if (enable && !currentModel) Toast.notify({ type: 'error', message: t('workflow.errorMsg.rerankModelRequired') }) onChange({ @@ -119,7 +119,7 @@ const RetrievalParamConfig: FC = ({ )}
diff --git a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx index 8a3dc1efba..619216d672 100644 --- a/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx +++ b/web/app/components/workflow/nodes/knowledge-retrieval/components/retrieval-config.tsx @@ -1,6 +1,6 @@ 'use client' import type { FC } from 'react' -import React, { useCallback, useState } from 'react' +import React, { useCallback, useMemo } from 'react' import { RiEqualizer2Line } from '@remixicon/react' import { useTranslation } from 'react-i18next' import type { MultipleRetrievalConfig, SingleRetrievalConfig } from '../types' @@ -14,8 +14,6 @@ import { import ConfigRetrievalContent from '@/app/components/app/configuration/dataset-config/params-config/config-content' import { RETRIEVE_TYPE } from '@/types/app' import { DATASET_DEFAULT } from '@/config' -import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks' -import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import Button from '@/app/components/base/button' import type { DatasetConfigs } from '@/models/debug' import type { DataSet } from '@/models/datasets' @@ -32,8 +30,8 @@ type Props = { onSingleRetrievalModelChange?: (config: ModelConfig) => void onSingleRetrievalModelParamsChange?: (config: ModelConfig) => void readonly?: boolean - openFromProps?: boolean - onOpenFromPropsChange?: (openFromProps: boolean) => void + rerankModalOpen: boolean + onRerankModelOpenChange: (open: boolean) => void selectedDatasets: DataSet[] } @@ -45,26 +43,52 @@ const RetrievalConfig: FC = ({ onSingleRetrievalModelChange, onSingleRetrievalModelParamsChange, readonly, - openFromProps, - onOpenFromPropsChange, + rerankModalOpen, + onRerankModelOpenChange, selectedDatasets, }) => { const { t } = useTranslation() - const [open, setOpen] = useState(false) - const mergedOpen = openFromProps !== undefined ? openFromProps : open + const { retrieval_mode, multiple_retrieval_config } = payload const handleOpen = useCallback((newOpen: boolean) => { - setOpen(newOpen) - onOpenFromPropsChange?.(newOpen) - }, [onOpenFromPropsChange]) + onRerankModelOpenChange(newOpen) + }, [onRerankModelOpenChange]) - const { - currentProvider: validRerankDefaultProvider, - currentModel: validRerankDefaultModel, - } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.rerank) + const datasetConfigs = useMemo(() => { + const { + reranking_model, + top_k, + score_threshold, + reranking_mode, + weights, + reranking_enable, + } = multiple_retrieval_config || {} + + return { + retrieval_model: retrieval_mode, + reranking_model: (reranking_model?.provider && reranking_model?.model) + ? { + reranking_provider_name: reranking_model?.provider, + reranking_model_name: reranking_model?.model, + } + : { + reranking_provider_name: '', + reranking_model_name: '', + }, + top_k: top_k || DATASET_DEFAULT.top_k, + score_threshold_enabled: !(score_threshold === undefined || score_threshold === null), + score_threshold, + datasets: { + datasets: [], + }, + reranking_mode, + weights, + reranking_enable, + } + }, [retrieval_mode, multiple_retrieval_config]) - const { multiple_retrieval_config } = payload const handleChange = useCallback((configs: DatasetConfigs, isRetrievalModeChange?: boolean) => { + // Legacy code, for compatibility, have to keep it if (isRetrievalModeChange) { onRetrievalModeChange(configs.retrieval_model) return @@ -72,13 +96,11 @@ const RetrievalConfig: FC = ({ onMultipleRetrievalConfigChange({ top_k: configs.top_k, score_threshold: configs.score_threshold_enabled ? (configs.score_threshold ?? DATASET_DEFAULT.score_threshold) : null, - reranking_model: payload.retrieval_mode === RETRIEVE_TYPE.oneWay + reranking_model: retrieval_mode === RETRIEVE_TYPE.oneWay ? undefined + // eslint-disable-next-line sonarjs/no-nested-conditional : (!configs.reranking_model?.reranking_provider_name - ? { - provider: validRerankDefaultProvider?.provider || '', - model: validRerankDefaultModel?.model || '', - } + ? undefined : { provider: configs.reranking_model?.reranking_provider_name, model: configs.reranking_model?.reranking_model_name, @@ -87,11 +109,11 @@ const RetrievalConfig: FC = ({ weights: configs.weights, reranking_enable: configs.reranking_enable, }) - }, [onMultipleRetrievalConfigChange, payload.retrieval_mode, validRerankDefaultProvider, validRerankDefaultModel, onRetrievalModeChange]) + }, [onMultipleRetrievalConfigChange, retrieval_mode, onRetrievalModeChange]) return ( = ({ onClick={() => { if (readonly) return - handleOpen(!mergedOpen) + handleOpen(!rerankModalOpen) }} >
)}
diff --git a/web/app/components/base/emoji-picker/index.tsx b/web/app/components/base/emoji-picker/index.tsx index d3b20bb507..7b91c62797 100644 --- a/web/app/components/base/emoji-picker/index.tsx +++ b/web/app/components/base/emoji-picker/index.tsx @@ -45,7 +45,7 @@ const EmojiPicker: FC = ({
@@ -54,7 +54,7 @@ const EmojiPicker: FC = ({ variant="primary" className='w-full' onClick={() => { - onSelect && onSelect(selectedEmoji, selectedBackground!) + onSelect?.(selectedEmoji, selectedBackground!) }}> {t('app.iconPicker.ok')} diff --git a/web/app/components/base/form/components/field/select.tsx b/web/app/components/base/form/components/field/select.tsx index f12b90335b..dee047e2eb 100644 --- a/web/app/components/base/form/components/field/select.tsx +++ b/web/app/components/base/form/components/field/select.tsx @@ -33,7 +33,10 @@ const SelectField = ({ field.handleChange(value)} + onChange={(value) => { + field.handleChange(value) + onChange?.(value) + }} {...selectProps} />
diff --git a/web/app/components/base/image-uploader/image-list.tsx b/web/app/components/base/image-uploader/image-list.tsx index 758ffe99d5..3b5f6dee9c 100644 --- a/web/app/components/base/image-uploader/image-list.tsx +++ b/web/app/components/base/image-uploader/image-list.tsx @@ -62,7 +62,7 @@ const ImageList: FC = ({ {item.progress === -1 && ( onReUpload && onReUpload(item._id)} + onClick={() => onReUpload?.(item._id)} /> )}
@@ -122,7 +122,7 @@ const ImageList: FC = ({ 'rounded-2xl shadow-lg hover:bg-state-base-hover', item.progress === -1 ? 'flex' : 'hidden group-hover:flex', )} - onClick={() => onRemove && onRemove(item._id)} + onClick={() => onRemove?.(item._id)} > diff --git a/web/app/components/base/image-uploader/image-preview.tsx b/web/app/components/base/image-uploader/image-preview.tsx index e67edaa3ca..53c22e344f 100644 --- a/web/app/components/base/image-uploader/image-preview.tsx +++ b/web/app/components/base/image-uploader/image-preview.tsx @@ -20,7 +20,7 @@ const isBase64 = (str: string): boolean => { try { return btoa(atob(str)) === str } - catch (err) { + catch { return false } } diff --git a/web/app/components/base/markdown-blocks/code-block.tsx b/web/app/components/base/markdown-blocks/code-block.tsx index 6814659a00..bc41c65fd5 100644 --- a/web/app/components/base/markdown-blocks/code-block.tsx +++ b/web/app/components/base/markdown-blocks/code-block.tsx @@ -127,7 +127,7 @@ const CodeBlock: any = memo(({ inline, className, children = '', ...props }: any // Store event handlers in useMemo to avoid recreating them const echartsEvents = useMemo(() => ({ - finished: (params: EChartsEventParams) => { + finished: (_params: EChartsEventParams) => { // Limit finished event frequency to avoid infinite loops finishedEventCountRef.current++ if (finishedEventCountRef.current > 3) { diff --git a/web/app/components/base/mermaid/utils.ts b/web/app/components/base/mermaid/utils.ts index 9d56494227..7e59869de1 100644 --- a/web/app/components/base/mermaid/utils.ts +++ b/web/app/components/base/mermaid/utils.ts @@ -60,7 +60,7 @@ export function svgToBase64(svgGraph: string): Promise { reader.readAsDataURL(blob) }) } - catch (error) { + catch { return Promise.resolve('') } } diff --git a/web/app/components/base/pagination/hook.ts b/web/app/components/base/pagination/hook.ts index 32a2af8013..9b9d86a4ef 100644 --- a/web/app/components/base/pagination/hook.ts +++ b/web/app/components/base/pagination/hook.ts @@ -10,9 +10,7 @@ const usePagination = ({ edgePageCount, middlePagesSiblingCount, }: IPaginationProps): IUsePagination => { - const pages = new Array(totalPages) - .fill(0) - .map((_, i) => i + 1) + const pages = React.useMemo(() => Array.from({ length: totalPages }, (_, i) => i + 1), [totalPages]) const hasPreviousPage = currentPage > 1 const hasNextPage = currentPage < totalPages diff --git a/web/app/components/base/popover/index.tsx b/web/app/components/base/popover/index.tsx index 0e7c384564..41df06f43a 100644 --- a/web/app/components/base/popover/index.tsx +++ b/web/app/components/base/popover/index.tsx @@ -37,13 +37,16 @@ export default function CustomPopover({ const timeOutRef = useRef(null) const onMouseEnter = (isOpen: boolean) => { - timeOutRef.current && window.clearTimeout(timeOutRef.current) - !isOpen && buttonRef.current?.click() + if (timeOutRef.current != null) + window.clearTimeout(timeOutRef.current) + if (!isOpen) + buttonRef.current?.click() } const onMouseLeave = (isOpen: boolean) => { timeOutRef.current = window.setTimeout(() => { - isOpen && buttonRef.current?.click() + if (isOpen) + buttonRef.current?.click() }, timeoutDuration) } diff --git a/web/app/components/base/select/locale-signin.tsx b/web/app/components/base/select/locale-signin.tsx index 2d487c4be3..2ec491f4bf 100644 --- a/web/app/components/base/select/locale-signin.tsx +++ b/web/app/components/base/select/locale-signin.tsx @@ -43,7 +43,7 @@ export default function LocaleSigninSelect({ className={'group flex w-full items-center rounded-lg px-3 py-2 text-sm text-text-secondary data-[active]:bg-state-base-hover'} onClick={(evt) => { evt.preventDefault() - onChange && onChange(item.value) + onChange?.(item.value) }} > {item.name} diff --git a/web/app/components/base/select/locale.tsx b/web/app/components/base/select/locale.tsx index cc5662f53b..2033488435 100644 --- a/web/app/components/base/select/locale.tsx +++ b/web/app/components/base/select/locale.tsx @@ -43,7 +43,7 @@ export default function Select({ className={'group flex w-full items-center rounded-lg px-3 py-2 text-sm text-text-secondary data-[active]:bg-state-base-hover'} onClick={(evt) => { evt.preventDefault() - onChange && onChange(item.value) + onChange?.(item.value) }} > {item.name} diff --git a/web/app/components/base/tag-management/panel.tsx b/web/app/components/base/tag-management/panel.tsx index 79031e9f8a..3cfb68fef0 100644 --- a/web/app/components/base/tag-management/panel.tsx +++ b/web/app/components/base/tag-management/panel.tsx @@ -97,10 +97,13 @@ const Panel = (props: PanelProps) => { const removeTagIDs = value.filter(v => !selectedTagIDs.includes(v)) const selectedTags = tagList.filter(tag => selectedTagIDs.includes(tag.id)) onCacheUpdate(selectedTags) - Promise.all([ - ...(addTagIDs.length ? [bind(addTagIDs)] : []), - ...[removeTagIDs.length ? removeTagIDs.map(tagID => unbind(tagID)) : []], - ]).finally(() => { + const operations: Promise[] = [] + if (addTagIDs.length) + operations.push(bind(addTagIDs)) + if (removeTagIDs.length) + operations.push(...removeTagIDs.map(tagID => unbind(tagID))) + + Promise.all(operations).finally(() => { if (onChange) onChange() }) diff --git a/web/app/components/base/voice-input/index.tsx b/web/app/components/base/voice-input/index.tsx index 5a5400ad30..6587a61217 100644 --- a/web/app/components/base/voice-input/index.tsx +++ b/web/app/components/base/voice-input/index.tsx @@ -81,7 +81,8 @@ const VoiceInput = ({ setStartRecord(false) setStartConvert(true) recorder.current.stop() - drawRecordId.current && cancelAnimationFrame(drawRecordId.current) + if (drawRecordId.current) + cancelAnimationFrame(drawRecordId.current) drawRecordId.current = null const canvas = canvasRef.current! const ctx = ctxRef.current! diff --git a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx index 433b7851d7..57509b646f 100644 --- a/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx +++ b/web/app/components/datasets/create-from-pipeline/create-options/create-from-dsl-modal/uploader.tsx @@ -34,7 +34,8 @@ const Uploader: FC = ({ const handleDragEnter = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target !== dragRef.current && setDragging(true) + if (e.target !== dragRef.current) + setDragging(true) } const handleDragOver = (e: DragEvent) => { e.preventDefault() @@ -43,7 +44,8 @@ const Uploader: FC = ({ const handleDragLeave = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target === dragRef.current && setDragging(false) + if (e.target === dragRef.current) + setDragging(false) } const handleDrop = (e: DragEvent) => { e.preventDefault() diff --git a/web/app/components/datasets/create/file-uploader/index.tsx b/web/app/components/datasets/create/file-uploader/index.tsx index 4dfdbc4e96..e2bbad2776 100644 --- a/web/app/components/datasets/create/file-uploader/index.tsx +++ b/web/app/components/datasets/create/file-uploader/index.tsx @@ -185,7 +185,8 @@ const FileUploader = ({ const handleDragEnter = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target !== dragRef.current && setDragging(true) + if (e.target !== dragRef.current) + setDragging(true) } const handleDragOver = (e: DragEvent) => { e.preventDefault() @@ -194,7 +195,8 @@ const FileUploader = ({ const handleDragLeave = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target === dragRef.current && setDragging(false) + if (e.target === dragRef.current) + setDragging(false) } type FileWithPath = { relativePath?: string diff --git a/web/app/components/datasets/create/step-two/index.tsx b/web/app/components/datasets/create/step-two/index.tsx index 650dc9425a..4c2e129cd2 100644 --- a/web/app/components/datasets/create/step-two/index.tsx +++ b/web/app/components/datasets/create/step-two/index.tsx @@ -568,9 +568,9 @@ const StepTwo = ({ params, { onSuccess(data) { - updateIndexingTypeCache && updateIndexingTypeCache(indexType as string) - updateResultCache && updateResultCache(data) - updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string) + updateIndexingTypeCache?.(indexType as string) + updateResultCache?.(data) + updateRetrievalMethodCache?.(retrievalConfig.search_method as string) }, }, ) @@ -578,17 +578,18 @@ const StepTwo = ({ else { await createDocumentMutation.mutateAsync(params, { onSuccess(data) { - updateIndexingTypeCache && updateIndexingTypeCache(indexType as string) - updateResultCache && updateResultCache(data) - updateRetrievalMethodCache && updateRetrievalMethodCache(retrievalConfig.search_method as string) + updateIndexingTypeCache?.(indexType as string) + updateResultCache?.(data) + updateRetrievalMethodCache?.(retrievalConfig.search_method as string) }, }) } if (mutateDatasetRes) mutateDatasetRes() invalidDatasetList() - onStepChange && onStepChange(+1) - isSetting && onSave && onSave() + onStepChange?.(+1) + if (isSetting) + onSave?.() } useEffect(() => { @@ -1026,7 +1027,7 @@ const StepTwo = ({ {!isSetting ? (
- diff --git a/web/app/components/datasets/create/website/jina-reader/base/options-wrap.tsx b/web/app/components/datasets/create/website/jina-reader/base/options-wrap.tsx deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx index 0692680005..ad05f0729b 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/local-file/index.tsx @@ -200,7 +200,8 @@ const LocalFile = ({ const handleDragEnter = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target !== dragRef.current && setDragging(true) + if (e.target !== dragRef.current) + setDragging(true) } const handleDragOver = (e: DragEvent) => { e.preventDefault() @@ -209,7 +210,8 @@ const LocalFile = ({ const handleDragLeave = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target === dragRef.current && setDragging(false) + if (e.target === dragRef.current) + setDragging(false) } const handleDrop = useCallback((e: DragEvent) => { diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx index 28c9ae456e..cd410c4d1e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/base/crawled-result.tsx @@ -45,10 +45,13 @@ const CrawledResult = ({ const handleItemCheckChange = useCallback((item: CrawlResultItem) => { return (checked: boolean) => { - if (checked) - isMultipleChoice ? onSelectedChange([...checkedList, item]) : onSelectedChange([item]) - else - onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url)) + if (checked) { + if (isMultipleChoice) + onSelectedChange([...checkedList, item]) + else + onSelectedChange([item]) + } + else { onSelectedChange(checkedList.filter(checkedItem => checkedItem.source_url !== item.source_url)) } } }, [checkedList, onSelectedChange, isMultipleChoice]) diff --git a/web/app/components/datasets/documents/create-from-pipeline/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/index.tsx index 75c31acd1b..77b77700ca 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/index.tsx @@ -326,7 +326,10 @@ const CreateFormPipeline = () => { }, []) const handleSubmit = useCallback((data: Record) => { - isPreview.current ? handlePreviewChunks(data) : handleProcess(data) + if (isPreview.current) + handlePreviewChunks(data) + else + handleProcess(data) }, [handlePreviewChunks, handleProcess]) const handlePreviewFileChange = useCallback((file: DocumentItem) => { diff --git a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx index aaf9ed8ffd..7e8749f0bf 100644 --- a/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx +++ b/web/app/components/datasets/documents/detail/batch-modal/csv-uploader.tsx @@ -99,7 +99,8 @@ const CSVUploader: FC = ({ const handleDragEnter = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target !== dragRef.current && setDragging(true) + if (e.target !== dragRef.current) + setDragging(true) } const handleDragOver = (e: DragEvent) => { e.preventDefault() @@ -108,7 +109,8 @@ const CSVUploader: FC = ({ const handleDragLeave = (e: DragEvent) => { e.preventDefault() e.stopPropagation() - e.target === dragRef.current && setDragging(false) + if (e.target === dragRef.current) + setDragging(false) } const handleDrop = (e: DragEvent) => { e.preventDefault() diff --git a/web/app/components/datasets/documents/detail/completed/index.tsx b/web/app/components/datasets/documents/detail/completed/index.tsx index 726be7519a..8fa167f976 100644 --- a/web/app/components/datasets/documents/detail/completed/index.tsx +++ b/web/app/components/datasets/documents/detail/completed/index.tsx @@ -284,7 +284,8 @@ const Completed: FC = ({ onSuccess: () => { notify({ type: 'success', message: t('common.actionMsg.modifiedSuccessfully') }) resetList() - !segId && setSelectedSegmentIds([]) + if (!segId) + setSelectedSegmentIds([]) }, onError: () => { notify({ type: 'error', message: t('common.actionMsg.modifiedUnsuccessfully') }) @@ -438,7 +439,8 @@ const Completed: FC = ({ } else { resetList() - currentPage !== totalPages && setCurrentPage(totalPages) + if (currentPage !== totalPages) + setCurrentPage(totalPages) } }, [segmentListData, limit, currentPage, resetList]) @@ -491,7 +493,8 @@ const Completed: FC = ({ } else { resetChildList() - currentPage !== totalPages && setCurrentPage(totalPages) + if (currentPage !== totalPages) + setCurrentPage(totalPages) } }, [childChunkListData, limit, currentPage, resetChildList]) diff --git a/web/app/components/datasets/documents/detail/metadata/index.tsx b/web/app/components/datasets/documents/detail/metadata/index.tsx index 4cb5fe97e9..88c359f8b2 100644 --- a/web/app/components/datasets/documents/detail/metadata/index.tsx +++ b/web/app/components/datasets/documents/detail/metadata/index.tsx @@ -66,7 +66,7 @@ export const FieldInfo: FC = ({ ? displayedValue : inputType === 'select' ? onUpdate && onUpdate(value as string)} + onSelect={({ value }) => onUpdate?.(value as string)} items={selectOptions} defaultValue={value} className={s.select} @@ -75,7 +75,7 @@ export const FieldInfo: FC = ({ /> : inputType === 'textarea' ? onUpdate && onUpdate(e.target.value)} + onChange={e => onUpdate?.(e.target.value)} value={value} className={s.textArea} placeholder={`${t('datasetDocuments.metadata.placeholder.add')}${label}`} diff --git a/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx b/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx index e69481c3ea..1ab47be445 100644 --- a/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx +++ b/web/app/components/datasets/documents/detail/settings/pipeline-settings/index.tsx @@ -148,7 +148,10 @@ const PipelineSettings = ({ }, []) const handleSubmit = useCallback((data: Record) => { - isPreview.current ? handlePreviewChunks(data) : handleProcess(data) + if (isPreview.current) + handlePreviewChunks(data) + else + handleProcess(data) }, [handlePreviewChunks, handleProcess]) if (isFetchingLastRunData) { diff --git a/web/app/components/datasets/hit-testing/textarea.tsx b/web/app/components/datasets/hit-testing/textarea.tsx index a9d66b0cc1..0e9dd16d56 100644 --- a/web/app/components/datasets/hit-testing/textarea.tsx +++ b/web/app/components/datasets/hit-testing/textarea.tsx @@ -80,7 +80,8 @@ const TextAreaWithButton = ({ onUpdateList?.() } setLoading(false) - _onSubmit && _onSubmit() + if (_onSubmit) + _onSubmit() } const externalRetrievalTestingOnSubmit = async () => { diff --git a/web/app/components/datasets/list/dataset-card/index.tsx b/web/app/components/datasets/list/dataset-card/index.tsx index db8ee0226d..b1304e578e 100644 --- a/web/app/components/datasets/list/dataset-card/index.tsx +++ b/web/app/components/datasets/list/dataset-card/index.tsx @@ -157,12 +157,12 @@ const DatasetCard = ({ data-disable-nprogress={true} onClick={(e) => { e.preventDefault() - isExternalProvider - ? push(`/datasets/${dataset.id}/hitTesting`) - // eslint-disable-next-line sonarjs/no-nested-conditional - : isPipelineUnpublished - ? push(`/datasets/${dataset.id}/pipeline`) - : push(`/datasets/${dataset.id}/documents`) + if (isExternalProvider) + push(`/datasets/${dataset.id}/hitTesting`) + else if (isPipelineUnpublished) + push(`/datasets/${dataset.id}/pipeline`) + else + push(`/datasets/${dataset.id}/documents`) }} > {!dataset.embedding_available && ( diff --git a/web/app/components/datasets/loading.tsx b/web/app/components/datasets/loading.tsx index e69de29bb2..182c1f91de 100644 --- a/web/app/components/datasets/loading.tsx +++ b/web/app/components/datasets/loading.tsx @@ -0,0 +1,3 @@ +const DatasetsLoading = () => null + +export default DatasetsLoading diff --git a/web/app/components/datasets/preview/index.tsx b/web/app/components/datasets/preview/index.tsx index e69de29bb2..e71c440c20 100644 --- a/web/app/components/datasets/preview/index.tsx +++ b/web/app/components/datasets/preview/index.tsx @@ -0,0 +1,3 @@ +const DatasetPreview = () => null + +export default DatasetPreview diff --git a/web/app/components/header/account-setting/collapse/index.tsx b/web/app/components/header/account-setting/collapse/index.tsx index 2ad4a97cd1..44360df8cd 100644 --- a/web/app/components/header/account-setting/collapse/index.tsx +++ b/web/app/components/header/account-setting/collapse/index.tsx @@ -39,7 +39,7 @@ const Collapse = ({
{ items.map(item => ( -
onSelect && onSelect(item)}> +
onSelect?.(item)}> {renderItem(item)}
)) diff --git a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx index 29da0ffc0c..291ba013f7 100644 --- a/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx +++ b/web/app/components/header/account-setting/model-provider-page/provider-added-card/model-load-balancing-configs.tsx @@ -49,7 +49,7 @@ const ModelLoadBalancingConfigs = ({ provider, model, configurationMethod, - currentCustomConfigurationModelFixedFields, + currentCustomConfigurationModelFixedFields: _currentCustomConfigurationModelFixedFields, withSwitch = false, className, modelCredential, diff --git a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx index 3c79acb653..10c28507f7 100644 --- a/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx +++ b/web/app/components/plugins/plugin-detail-panel/app-selector/app-picker.tsx @@ -33,7 +33,7 @@ type Props = { } const AppPicker: FC = ({ - scope, + scope: _scope, disabled, trigger, placement = 'right-start', @@ -90,7 +90,7 @@ const AppPicker: FC = ({ } // Set up MutationObserver to watch DOM changes - mutationObserver = new MutationObserver((mutations) => { + mutationObserver = new MutationObserver((_mutations) => { if (observerTarget.current) { setupIntersectionObserver() mutationObserver?.disconnect() diff --git a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx index fb4c99e1e4..873f187e8f 100644 --- a/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx +++ b/web/app/components/plugins/plugin-detail-panel/model-selector/index.tsx @@ -148,7 +148,7 @@ const ModelParameterModal: FC = ({ }) } } - catch (e) { + catch { Toast.notify({ type: 'error', message: t('common.error') }) } } diff --git a/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts b/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts index d5c0797c9b..376894b1bb 100644 --- a/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts +++ b/web/app/components/rag-pipeline/components/panel/input-field/field-list/hooks.ts @@ -51,7 +51,7 @@ export const useFieldList = ({ const handleListSortChange = useCallback((list: SortableItem[]) => { const newInputFields = list.map((item) => { - const { id, chosen, selected, ...filed } = item + const { id: _id, chosen: _chosen, selected: _selected, ...filed } = item return filed }) handleInputFieldsChange(newInputFields) diff --git a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx index 16291f868b..a536f66137 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/header.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/header.tsx @@ -15,7 +15,8 @@ const Header = () => { isPreparingDataSource, setIsPreparingDataSource, } = workflowStore.getState() - isPreparingDataSource && setIsPreparingDataSource?.(false) + if (isPreparingDataSource) + setIsPreparingDataSource?.(false) handleCancelDebugAndPreviewPanel() }, [workflowStore]) diff --git a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts index 86e44dced7..ad757f36a7 100644 --- a/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/rag-pipeline/hooks/use-nodes-sync-draft.ts @@ -104,7 +104,7 @@ export const useNodesSyncDraft = () => { const res = await syncWorkflowDraft(postParams) setSyncWorkflowDraftHash(res.hash) setDraftUpdatedAt(res.updated_at) - callback?.onSuccess && callback.onSuccess() + callback?.onSuccess?.() } catch (error: any) { if (error && error.json && !error.bodyUsed) { @@ -113,10 +113,10 @@ export const useNodesSyncDraft = () => { handleRefreshWorkflowDraft() }) } - callback?.onError && callback.onError() + callback?.onError?.() } finally { - callback?.onSettled && callback.onSettled() + callback?.onSettled?.() } } }, [getPostParams, getNodesReadOnly, workflowStore, handleRefreshWorkflowDraft]) diff --git a/web/app/components/share/text-generation/index.tsx b/web/app/components/share/text-generation/index.tsx index da5b09b065..98804c7311 100644 --- a/web/app/components/share/text-generation/index.tsx +++ b/web/app/components/share/text-generation/index.tsx @@ -363,7 +363,8 @@ const TextGeneration: FC = ({ (async () => { if (!appData || !appParams) return - !isWorkflow && fetchSavedMessage() + if (!isWorkflow) + fetchSavedMessage() const { app_id: appId, site: siteInfo, custom_config } = appData setAppId(appId) setSiteInfo(siteInfo as SiteInfo) diff --git a/web/app/components/swr-initializer.tsx b/web/app/components/swr-initializer.tsx index 0a873400d6..fd9432fdd8 100644 --- a/web/app/components/swr-initializer.tsx +++ b/web/app/components/swr-initializer.tsx @@ -62,8 +62,10 @@ const SwrInitializer = ({ return } if (searchParams.has('access_token') || searchParams.has('refresh_token')) { - consoleToken && localStorage.setItem('console_token', consoleToken) - refreshToken && localStorage.setItem('refresh_token', refreshToken) + if (consoleToken) + localStorage.setItem('console_token', consoleToken) + if (refreshToken) + localStorage.setItem('refresh_token', refreshToken) const redirectUrl = resolvePostLoginRedirect(searchParams) if (redirectUrl) location.replace(redirectUrl) diff --git a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts index 654977fb15..5705deb0c0 100644 --- a/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts +++ b/web/app/components/workflow-app/hooks/use-nodes-sync-draft.ts @@ -124,7 +124,7 @@ export const useNodesSyncDraft = () => { const res = await syncWorkflowDraft(postParams) setSyncWorkflowDraftHash(res.hash) setDraftUpdatedAt(res.updated_at) - callback?.onSuccess && callback.onSuccess() + callback?.onSuccess?.() } catch (error: any) { if (error && error.json && !error.bodyUsed) { @@ -133,10 +133,10 @@ export const useNodesSyncDraft = () => { handleRefreshWorkflowDraft() }) } - callback?.onError && callback.onError() + callback?.onError?.() } finally { - callback?.onSettled && callback.onSettled() + callback?.onSettled?.() } } }, [workflowStore, getPostParams, getNodesReadOnly, handleRefreshWorkflowDraft]) diff --git a/web/app/components/workflow/block-selector/tool/tool.tsx b/web/app/components/workflow/block-selector/tool/tool.tsx index 6004bb119a..30d3e218d2 100644 --- a/web/app/components/workflow/block-selector/tool/tool.tsx +++ b/web/app/components/workflow/block-selector/tool/tool.tsx @@ -73,7 +73,7 @@ const Tool: FC = ({ if (isHovering && !isAllSelected) { return ( { + onClick={() => { onSelectMultiple?.(BlockEnum.Tool, actions.filter(action => !getIsDisabled(action)).map((tool) => { const params: Record = {} if (tool.parameters) { diff --git a/web/app/components/workflow/hooks/use-shortcuts.ts b/web/app/components/workflow/hooks/use-shortcuts.ts index b2d71555d7..a744fefd50 100644 --- a/web/app/components/workflow/hooks/use-shortcuts.ts +++ b/web/app/components/workflow/hooks/use-shortcuts.ts @@ -107,7 +107,8 @@ export const useShortcuts = (): void => { const { showDebugAndPreviewPanel } = workflowStore.getState() if (shouldHandleShortcut(e) && !showDebugAndPreviewPanel) { e.preventDefault() - workflowHistoryShortcutsEnabled && handleHistoryBack() + if (workflowHistoryShortcutsEnabled) + handleHistoryBack() } }, { exactMatch: true, useCapture: true }) @@ -116,7 +117,8 @@ export const useShortcuts = (): void => { (e) => { if (shouldHandleShortcut(e)) { e.preventDefault() - workflowHistoryShortcutsEnabled && handleHistoryForward() + if (workflowHistoryShortcutsEnabled) + handleHistoryForward() } }, { exactMatch: true, useCapture: true }, diff --git a/web/app/components/workflow/hooks/use-workflow.ts b/web/app/components/workflow/hooks/use-workflow.ts index 02a2f09d63..3f9f8106cf 100644 --- a/web/app/components/workflow/hooks/use-workflow.ts +++ b/web/app/components/workflow/hooks/use-workflow.ts @@ -354,7 +354,7 @@ export const useWorkflow = () => { return startNodes }, [nodesMap, getRootNodesById]) - const isValidConnection = useCallback(({ source, sourceHandle, target }: Connection) => { + const isValidConnection = useCallback(({ source, sourceHandle: _sourceHandle, target }: Connection) => { const { edges, getNodes, diff --git a/web/app/components/workflow/nodes/_base/components/retry/utils.ts b/web/app/components/workflow/nodes/_base/components/retry/utils.ts index e69de29bb2..336ce12bb9 100644 --- a/web/app/components/workflow/nodes/_base/components/retry/utils.ts +++ b/web/app/components/workflow/nodes/_base/components/retry/utils.ts @@ -0,0 +1 @@ +export {} diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx index 273e100f8e..ba27d023e7 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-picker.tsx @@ -407,7 +407,10 @@ const VarReferencePicker: FC = ({ { if (readonly) return - !isConstant ? setOpen(!open) : setControlFocus(Date.now()) + if (!isConstant) + setOpen(!open) + else + setControlFocus(Date.now()) }} className='group/picker-trigger-wrap relative !flex'> <> {isAddBtnTrigger @@ -457,7 +460,10 @@ const VarReferencePicker: FC = ({ onClick={() => { if (readonly) return - !isConstant ? setOpen(!open) : setControlFocus(Date.now()) + if (!isConstant) + setOpen(!open) + else + setControlFocus(Date.now()) }} className='h-full grow' > diff --git a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx index 067dbf8652..e70cfed97c 100644 --- a/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx +++ b/web/app/components/workflow/nodes/_base/components/variable/var-reference-vars.tsx @@ -137,7 +137,7 @@ const Item: FC = ({ const isHovering = isItemHovering || isChildrenHovering const open = (isObj || isStructureOutput) && isHovering useEffect(() => { - onHovering && onHovering(isHovering) + onHovering?.(isHovering) }, [isHovering]) const handleChosen = (e: React.MouseEvent) => { e.stopPropagation() diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx index 06962389c9..b26dd74714 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/last-run/index.tsx @@ -25,12 +25,12 @@ type Props = { } & Partial const LastRun: FC = ({ - appId, + appId: _appId, nodeId, canSingleRun, isRunAfterSingleRun, updateNodeRunningStatus, - nodeInfo, + nodeInfo: _nodeInfo, runningStatus: oneStepRunRunningStatus, onSingleRunClicked, singleRunResult, diff --git a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx index 789e24835f..99ee0d25b5 100644 --- a/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx +++ b/web/app/components/workflow/nodes/knowledge-base/components/option-card.tsx @@ -88,7 +88,8 @@ const OptionCard = memo(({ )} onClick={(e) => { e.stopPropagation() - !readonly && enableSelect && id && onClick?.(id) + if (!readonly && enableSelect && id) + onClick?.(id) }} >
= ({ const handleResetDefaults = useCallback(() => { if (currentTab === SchemaView.VisualEditor) { setHoveringProperty(null) - advancedEditing && setAdvancedEditing(false) - isAddingNewField && setIsAddingNewField(false) + if (advancedEditing) + setAdvancedEditing(false) + if (isAddingNewField) + setIsAddingNewField(false) } setJsonSchema(DEFAULT_SCHEMA) setJson(JSON.stringify(DEFAULT_SCHEMA, null, 2)) diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx index ae72d494d1..1db31cd470 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/edit-card/index.tsx @@ -87,8 +87,10 @@ const EditCard: FC = ({ }) useSubscribe('fieldChangeSuccess', () => { - isAddingNewField && setIsAddingNewField(false) - advancedEditing && setAdvancedEditing(false) + if (isAddingNewField) + setIsAddingNewField(false) + if (advancedEditing) + setAdvancedEditing(false) }) const emitPropertyNameChange = useCallback(() => { diff --git a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts index 8256a3c862..4f7e1e6f00 100644 --- a/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts +++ b/web/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/hooks.ts @@ -45,8 +45,10 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => { onChange(backupSchema) setBackupSchema(null) } - isAddingNewField && setIsAddingNewField(false) - advancedEditing && setAdvancedEditing(false) + if (isAddingNewField) + setIsAddingNewField(false) + if (advancedEditing) + setAdvancedEditing(false) setHoveringProperty(null) }) @@ -221,7 +223,8 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => { }) useSubscribe('addField', (params) => { - advancedEditing && setAdvancedEditing(false) + if (advancedEditing) + setAdvancedEditing(false) setBackupSchema(jsonSchema) const { path } = params as AddEventParams setIsAddingNewField(true) diff --git a/web/app/components/workflow/nodes/loop/components/condition-value.tsx b/web/app/components/workflow/nodes/loop/components/condition-value.tsx index 922cff4a61..2f011f870a 100644 --- a/web/app/components/workflow/nodes/loop/components/condition-value.tsx +++ b/web/app/components/workflow/nodes/loop/components/condition-value.tsx @@ -22,7 +22,7 @@ type ConditionValueProps = { } const ConditionValue = ({ variableSelector, - labelName, + labelName: _labelName, operator, value, }: ConditionValueProps) => { diff --git a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx index 07eb79a269..9d19b61093 100644 --- a/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx +++ b/web/app/components/workflow/panel/chat-variable-panel/components/variable-modal-trigger.tsx @@ -35,7 +35,8 @@ const VariableModalTrigger = ({ open={open} onOpenChange={() => { setOpen(v => !v) - open && onClose() + if (open) + onClose() }} placement='left-start' offset={{ @@ -45,7 +46,8 @@ const VariableModalTrigger = ({ > { setOpen(v => !v) - open && onClose() + if (open) + onClose() }}>