From abc61f680a348f85f2e5a0f461079087c3314af0 Mon Sep 17 00:00:00 2001 From: kenwoodjw Date: Sat, 10 May 2025 18:17:05 +0800 Subject: [PATCH 01/34] fix delete api response (#19480) Signed-off-by: kenwoodjw --- .../datasets/template/template.en.mdx | 18 ++++++----------- .../datasets/template/template.ja.mdx | 20 +++++++------------ .../datasets/template/template.zh.mdx | 18 ++++++----------- .../develop/template/template.zh.mdx | 4 ++-- .../template/template_advanced_chat.en.mdx | 10 ++++------ .../template/template_advanced_chat.ja.mdx | 6 ++---- .../template/template_advanced_chat.zh.mdx | 10 ++++------ .../develop/template/template_chat.en.mdx | 10 ++++------ .../develop/template/template_chat.ja.mdx | 6 ++---- .../develop/template/template_chat.zh.mdx | 6 ++---- 10 files changed, 39 insertions(+), 69 deletions(-) diff --git a/web/app/(commonLayout)/datasets/template/template.en.mdx b/web/app/(commonLayout)/datasets/template/template.en.mdx index 54e08b45d8..b8e29334f2 100644 --- a/web/app/(commonLayout)/datasets/template/template.en.mdx +++ b/web/app/(commonLayout)/datasets/template/template.en.mdx @@ -1040,10 +1040,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1335,10 +1333,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1620,10 +1616,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/(commonLayout)/datasets/template/template.ja.mdx b/web/app/(commonLayout)/datasets/template/template.ja.mdx index 6691d902a8..b7b9a4a62e 100644 --- a/web/app/(commonLayout)/datasets/template/template.ja.mdx +++ b/web/app/(commonLayout)/datasets/template/template.ja.mdx @@ -501,7 +501,7 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```text {{ title: 'Response' }} + ```text {{ title: 'レスポンス' }} 204 No Content ``` @@ -797,10 +797,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'レスポンス' }} + 204 No Content ``` @@ -1092,10 +1090,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'レスポンス' }} + 204 No Content ``` @@ -1377,10 +1373,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'レスポンス' }} + 204 No Content ``` diff --git a/web/app/(commonLayout)/datasets/template/template.zh.mdx b/web/app/(commonLayout)/datasets/template/template.zh.mdx index a8bb7046e6..4ce80dd5c7 100644 --- a/web/app/(commonLayout)/datasets/template/template.zh.mdx +++ b/web/app/(commonLayout)/datasets/template/template.zh.mdx @@ -1047,10 +1047,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1342,10 +1340,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1628,10 +1624,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, PropertyInstructi ``` - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template.zh.mdx b/web/app/components/develop/template/template.zh.mdx index 447dc08396..13aa373485 100755 --- a/web/app/components/develop/template/template.zh.mdx +++ b/web/app/components/develop/template/template.zh.mdx @@ -738,8 +738,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - ```json {{ title: 'Response' }} - {"result": "success"} + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_advanced_chat.en.mdx b/web/app/components/develop/template/template_advanced_chat.en.mdx index 0d10af6a71..b77e0528d7 100644 --- a/web/app/components/develop/template/template_advanced_chat.en.mdx +++ b/web/app/components/develop/template/template_advanced_chat.en.mdx @@ -765,10 +765,8 @@ Chat applications support session persistence, allowing previous chat history to - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1432,8 +1430,8 @@ Chat applications support session persistence, allowing previous chat history to - ```json {{ title: 'Response' }} - {"result": "success"} + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_advanced_chat.ja.mdx b/web/app/components/develop/template/template_advanced_chat.ja.mdx index db869f2093..db9f65db5b 100644 --- a/web/app/components/develop/template/template_advanced_chat.ja.mdx +++ b/web/app/components/develop/template/template_advanced_chat.ja.mdx @@ -764,10 +764,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - ```json {{ title: '応答' }} - { - "result": "success" - } + ```text {{ title: '応答' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_advanced_chat.zh.mdx b/web/app/components/develop/template/template_advanced_chat.zh.mdx index e634130a4b..6d1876e4d1 100755 --- a/web/app/components/develop/template/template_advanced_chat.zh.mdx +++ b/web/app/components/develop/template/template_advanced_chat.zh.mdx @@ -799,10 +799,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1456,8 +1454,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - ```json {{ title: 'Response' }} - {"result": "success"} + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_chat.en.mdx b/web/app/components/develop/template/template_chat.en.mdx index aa3b9c62e5..2386eed0bc 100644 --- a/web/app/components/develop/template/template_chat.en.mdx +++ b/web/app/components/develop/template/template_chat.en.mdx @@ -798,10 +798,8 @@ Chat applications support session persistence, allowing previous chat history to - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` @@ -1472,8 +1470,8 @@ Chat applications support session persistence, allowing previous chat history to - ```json {{ title: 'Response' }} - {"result": "success"} + ```text {{ title: 'Response' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_chat.ja.mdx b/web/app/components/develop/template/template_chat.ja.mdx index 7ad9a7fe3e..86ad9d21f2 100644 --- a/web/app/components/develop/template/template_chat.ja.mdx +++ b/web/app/components/develop/template/template_chat.ja.mdx @@ -797,10 +797,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty, Paragraph } from - ```json {{ title: '応答' }} - { - "result": "success" - } + ```text {{ title: '応答' }} + 204 No Content ``` diff --git a/web/app/components/develop/template/template_chat.zh.mdx b/web/app/components/develop/template/template_chat.zh.mdx index a49f70a514..c63f8d1c88 100644 --- a/web/app/components/develop/template/template_chat.zh.mdx +++ b/web/app/components/develop/template/template_chat.zh.mdx @@ -811,10 +811,8 @@ import { Row, Col, Properties, Property, Heading, SubProperty } from '../md.tsx' - ```json {{ title: 'Response' }} - { - "result": "success" - } + ```text {{ title: 'Response' }} + 204 No Content ``` From af12cf1bf6679a63fdf1e65676880550f88ee4b1 Mon Sep 17 00:00:00 2001 From: GQ1994 <957534377@qq.com> Date: Sat, 10 May 2025 18:17:16 +0800 Subject: [PATCH 02/34] fix_invitation-link.tsx_url_more_basepath_bug (#19453) Co-authored-by: qingguo --- .../members-page/invited-modal/invitation-link.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx index 0ef1b14569..2426f7b933 100644 --- a/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx +++ b/web/app/components/header/account-setting/members-page/invited-modal/invitation-link.tsx @@ -1,6 +1,5 @@ 'use client' import React, { useCallback, useEffect, useRef, useState } from 'react' -import { basePath } from '@/utils/var' import { t } from 'i18next' import copy from 'copy-to-clipboard' import s from './index.module.css' @@ -19,7 +18,8 @@ const InvitationLink = ({ const selector = useRef(`invite-link-${randomString(4)}`) const copyHandle = useCallback(() => { - copy(`${!value.url.startsWith('http') ? window.location.origin : ''}${basePath}${value.url}`) + // No prefix is needed here because the backend has already processed it + copy(`${!value.url.startsWith('http') ? window.location.origin : ''}${value.url}`) setIsCopied(true) }, [value]) @@ -42,7 +42,7 @@ const InvitationLink = ({ -
{basePath + value.url}
+
{value.url}
From b29087b6800907a141c2caed8db7269b3fc3f794 Mon Sep 17 00:00:00 2001 From: crazywoola <100913391+crazywoola@users.noreply.github.com> Date: Sat, 10 May 2025 19:43:56 +0800 Subject: [PATCH 03/34] fix: db.session.query(TenantAccountJoin) (#19482) --- api/models/account.py | 2 +- .../mail_clean_document_notify_task.py | 4 ++- api/services/account_service.py | 29 +++++++++++++------ 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/api/models/account.py b/api/models/account.py index de7ede8d17..bb6a2a4735 100644 --- a/api/models/account.py +++ b/api/models/account.py @@ -52,7 +52,7 @@ class Account(UserMixin, Base): @current_tenant.setter def current_tenant(self, value: "Tenant"): tenant = value - ta = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=self.id).first() + ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=self.id).first() if ta: tenant.current_role = ta.role else: diff --git a/api/schedule/mail_clean_document_notify_task.py b/api/schedule/mail_clean_document_notify_task.py index b3d0e09784..29d86935b5 100644 --- a/api/schedule/mail_clean_document_notify_task.py +++ b/api/schedule/mail_clean_document_notify_task.py @@ -47,7 +47,9 @@ def mail_clean_document_notify_task(): if not tenant: continue # check current owner - current_owner_join = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, role="owner").first() + current_owner_join = ( + db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, role="owner").first() + ) if not current_owner_join: continue account = Account.query.filter(Account.id == current_owner_join.account_id).first() diff --git a/api/services/account_service.py b/api/services/account_service.py index eca706a3d2..da03da69e9 100644 --- a/api/services/account_service.py +++ b/api/services/account_service.py @@ -615,7 +615,10 @@ class TenantService: ): """Check if user have a workspace or not""" available_ta = ( - TenantAccountJoin.query.filter_by(account_id=account.id).order_by(TenantAccountJoin.id.asc()).first() + db.session.query(TenantAccountJoin) + .filter_by(account_id=account.id) + .order_by(TenantAccountJoin.id.asc()) + .first() ) if available_ta: @@ -669,7 +672,7 @@ class TenantService: if not tenant: raise TenantNotFoundError("Tenant not found.") - ta = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=account.id).first() + ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=account.id).first() if ta: tenant.role = ta.role else: @@ -698,7 +701,7 @@ class TenantService: if not tenant_account_join: raise AccountNotLinkTenantError("Tenant not found or account is not a member of the tenant.") else: - TenantAccountJoin.query.filter( + db.session.query(TenantAccountJoin).filter( TenantAccountJoin.account_id == account.id, TenantAccountJoin.tenant_id != tenant_id ).update({"current": False}) tenant_account_join.current = True @@ -790,7 +793,7 @@ class TenantService: if operator.id == member.id: raise CannotOperateSelfError("Cannot operate self.") - ta_operator = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=operator.id).first() + ta_operator = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=operator.id).first() if not ta_operator or ta_operator.role not in perms[action]: raise NoPermissionError(f"No permission to {action} member.") @@ -803,7 +806,7 @@ class TenantService: TenantService.check_member_permission(tenant, operator, account, "remove") - ta = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=account.id).first() + ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=account.id).first() if not ta: raise MemberNotInTenantError("Member not in tenant.") @@ -815,15 +818,23 @@ class TenantService: """Update member role""" TenantService.check_member_permission(tenant, operator, member, "update") - target_member_join = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=member.id).first() + target_member_join = ( + db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=member.id).first() + ) + + if not target_member_join: + raise MemberNotInTenantError("Member not in tenant.") if target_member_join.role == new_role: raise RoleAlreadyAssignedError("The provided role is already assigned to the member.") if new_role == "owner": # Find the current owner and change their role to 'admin' - current_owner_join = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, role="owner").first() - current_owner_join.role = "admin" + current_owner_join = ( + db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, role="owner").first() + ) + if current_owner_join: + current_owner_join.role = "admin" # Update the role of the target member target_member_join.role = new_role @@ -962,7 +973,7 @@ class RegisterService: TenantService.switch_tenant(account, tenant.id) else: TenantService.check_member_permission(tenant, inviter, account, "add") - ta = TenantAccountJoin.query.filter_by(tenant_id=tenant.id, account_id=account.id).first() + ta = db.session.query(TenantAccountJoin).filter_by(tenant_id=tenant.id, account_id=account.id).first() if not ta: TenantService.create_tenant_member(tenant, account, role) From 75259c1ea15953857a720116caa385d3d498736e Mon Sep 17 00:00:00 2001 From: zxhlyh Date: Sat, 10 May 2025 20:00:43 +0800 Subject: [PATCH 04/34] chore: update edu version text (#19485) --- web/i18n/en-US/education.ts | 6 +++--- web/i18n/zh-Hans/education.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/web/i18n/en-US/education.ts b/web/i18n/en-US/education.ts index b3a13612ed..ea125a1332 100644 --- a/web/i18n/en-US/education.ts +++ b/web/i18n/en-US/education.ts @@ -2,7 +2,7 @@ const translation = { toVerified: 'Get Education Verified', toVerifiedTip: { front: 'You are now eligible for Education Verified status. Please enter your education information below to complete the process and receive an', - coupon: 'exclusive 50% coupon', + coupon: 'exclusive 100% coupon', end: 'for the Dify Professional Plan.', }, currentSigned: 'CURRENTLY SIGNED IN AS', @@ -38,9 +38,9 @@ const translation = { submitError: 'Form submission failed. Please try again later.', learn: 'Learn how to get education verified', successTitle: 'You Have Got Dify Education Verified', - successContent: 'We have issued a 50% discount coupon for the Dify Professional plan to your account. The coupon is valid for one year, please use it within the validity period.', + successContent: 'We have issued a 100% discount coupon for the Dify Professional plan to your account. The coupon is valid for one year, please use it within the validity period.', rejectTitle: 'Your Dify Educational Verification Has Been Rejected', - rejectContent: 'Unfortunately, you are not eligible for Education Verified status and therefore cannot receive the exclusive 50% coupon for the Dify Professional Plan if you use this email address.', + rejectContent: 'Unfortunately, you are not eligible for Education Verified status and therefore cannot receive the exclusive 100% coupon for the Dify Professional Plan if you use this email address.', emailLabel: 'Your current email', } diff --git a/web/i18n/zh-Hans/education.ts b/web/i18n/zh-Hans/education.ts index ca4d2cb3cc..6bfcd9177c 100644 --- a/web/i18n/zh-Hans/education.ts +++ b/web/i18n/zh-Hans/education.ts @@ -2,7 +2,7 @@ const translation = { toVerified: '获取教育版认证', toVerifiedTip: { front: '您现在符合教育版认证的资格。请在下方输入您的教育信息,以完成认证流程,并领取 Dify Professional 版的', - coupon: '50% 独家优惠券', + coupon: '100% 独家优惠券', end: '。', }, currentSigned: '您当前登录的账户是', @@ -38,9 +38,9 @@ const translation = { submitError: '提交表单失败,请稍后重新提交问卷。', learn: '了解如何获取教育版认证', successTitle: '您已成功获得 Dify 教育版认证!', - successContent: '我们已向您的账户发放 Dify Professional 版 50% 折扣优惠券。该优惠券有效期为一年,请在有效期内使用。', + successContent: '我们已向您的账户发放 Dify Professional 版 100% 折扣优惠券。该优惠券有效期为一年,请在有效期内使用。', rejectTitle: '您的 Dify 教育版认证已被拒绝', - rejectContent: '非常遗憾,您无法使用此电子邮件以获得教育版认证资格,也无法领取 Dify Professional 版的 50% 独家优惠券。', + rejectContent: '非常遗憾,您无法使用此电子邮件以获得教育版认证资格,也无法领取 Dify Professional 版的 100% 独家优惠券。', emailLabel: '您当前的邮箱', } From c431da9571bf27c10c8f7799e077e72f87b861b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=9B=E9=9C=B2=E5=85=88=E7=94=9F?= Date: Sat, 10 May 2025 20:01:31 +0800 Subject: [PATCH 05/34] sort extensions for review, (#19470) Signed-off-by: zhanluxianshen --- api/app_factory.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/api/app_factory.py b/api/app_factory.py index 1c886ac5c7..d63aa83207 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -62,27 +62,27 @@ def initialize_extensions(app: DifyApp): ) extensions = [ - ext_timezone, - ext_logging, - ext_warnings, - ext_import_modules, - ext_set_secretkey, - ext_compress, - ext_code_based_extension, - ext_database, ext_app_metrics, - ext_migrate, - ext_redis, - ext_storage, + ext_blueprints, ext_celery, + ext_code_based_extension, + ext_commands, + ext_compress, + ext_database, + ext_hosting_provider, + ext_import_modules, + ext_logging, ext_login, ext_mail, - ext_hosting_provider, - ext_sentry, - ext_proxy_fix, - ext_blueprints, - ext_commands, + ext_migrate, ext_otel, + ext_proxy_fix, + ext_redis, + ext_sentry, + ext_set_secretkey, + ext_storage, + ext_timezone, + ext_warnings, ] for ext in extensions: short_name = ext.__name__.split(".")[-1] From 505d4cce78d86898fd704c35491f322883e97e0d 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, 11 May 2025 16:57:13 +0800 Subject: [PATCH 06/34] Revert "sort extensions for review," (#19496) --- api/app_factory.py | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/api/app_factory.py b/api/app_factory.py index d63aa83207..1c886ac5c7 100644 --- a/api/app_factory.py +++ b/api/app_factory.py @@ -62,27 +62,27 @@ def initialize_extensions(app: DifyApp): ) extensions = [ - ext_app_metrics, - ext_blueprints, - ext_celery, - ext_code_based_extension, - ext_commands, - ext_compress, - ext_database, - ext_hosting_provider, - ext_import_modules, + ext_timezone, ext_logging, + ext_warnings, + ext_import_modules, + ext_set_secretkey, + ext_compress, + ext_code_based_extension, + ext_database, + ext_app_metrics, + ext_migrate, + ext_redis, + ext_storage, + ext_celery, ext_login, ext_mail, - ext_migrate, - ext_otel, - ext_proxy_fix, - ext_redis, + ext_hosting_provider, ext_sentry, - ext_set_secretkey, - ext_storage, - ext_timezone, - ext_warnings, + ext_proxy_fix, + ext_blueprints, + ext_commands, + ext_otel, ] for ext in extensions: short_name = ext.__name__.split(".")[-1] From 87da1554779a8dd5a5205fecc656649586c451ed Mon Sep 17 00:00:00 2001 From: yangzheli <43645580+yangzheli@users.noreply.github.com> Date: Mon, 12 May 2025 09:00:57 +0800 Subject: [PATCH 07/34] fix: agent log modal fails to open and the timer is not cleared (#18900) (#19471) --- web/app/components/base/chat/chat/hooks.ts | 2 ++ web/app/components/base/chat/chat/type.ts | 1 + .../components/base/markdown-blocks/think-block.tsx | 10 ++++------ 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/web/app/components/base/chat/chat/hooks.ts b/web/app/components/base/chat/chat/hooks.ts index bff222ea38..fde4674539 100644 --- a/web/app/components/base/chat/chat/hooks.ts +++ b/web/app/components/base/chat/chat/hooks.ts @@ -424,6 +424,8 @@ export const useChat = ( const response = responseItem as any if (thought.message_id && !hasSetResponseId) response.id = thought.message_id + if (thought.conversation_id) + response.conversationId = thought.conversation_id if (response.agent_thoughts.length === 0) { response.agent_thoughts.push(thought) diff --git a/web/app/components/base/chat/chat/type.ts b/web/app/components/base/chat/chat/type.ts index 7f22ba05b7..b37151f8f9 100644 --- a/web/app/components/base/chat/chat/type.ts +++ b/web/app/components/base/chat/chat/type.ts @@ -41,6 +41,7 @@ export type ThoughtItem = { tool_input: string tool_labels?: { [key: string]: TypeWithI18N } message_id: string + conversation_id: string observation: string position: number files?: string[] diff --git a/web/app/components/base/markdown-blocks/think-block.tsx b/web/app/components/base/markdown-blocks/think-block.tsx index 282282db2b..565582e326 100644 --- a/web/app/components/base/markdown-blocks/think-block.tsx +++ b/web/app/components/base/markdown-blocks/think-block.tsx @@ -41,9 +41,10 @@ const useThinkTimer = (children: any) => { const timerRef = useRef() useEffect(() => { + if (isComplete) return + timerRef.current = setInterval(() => { - if (!isComplete) - setElapsedTime(Math.floor((Date.now() - startTime) / 100) / 10) + setElapsedTime(Math.floor((Date.now() - startTime) / 100) / 10) }, 100) return () => { @@ -53,11 +54,8 @@ const useThinkTimer = (children: any) => { }, [startTime, isComplete]) useEffect(() => { - if (hasEndThink(children)) { + if (hasEndThink(children)) setIsComplete(true) - if (timerRef.current) - clearInterval(timerRef.current) - } }, [children]) return { elapsedTime, isComplete } From c720e0dd0453619465da6a306ad0a695cb9bec78 Mon Sep 17 00:00:00 2001 From: LeanDeR <34670582+auxpd@users.noreply.github.com> Date: Mon, 12 May 2025 09:32:41 +0800 Subject: [PATCH 08/34] refactor(workflow): revamp logging module for loop & iteration nodes (#19484) --- .../nodes/iteration/iteration_node.py | 29 ++++--- api/core/workflow/nodes/loop/loop_node.py | 2 +- .../iteration-log/iteration-log-trigger.tsx | 72 ++++++++++++++--- .../run/loop-log/loop-log-trigger.tsx | 81 +++++++++++++++---- web/app/components/workflow/run/node.tsx | 4 + .../components/workflow/run/tracing-panel.tsx | 1 + web/i18n/en-US/workflow.ts | 3 + web/i18n/zh-Hans/workflow.ts | 3 + 8 files changed, 149 insertions(+), 46 deletions(-) diff --git a/api/core/workflow/nodes/iteration/iteration_node.py b/api/core/workflow/nodes/iteration/iteration_node.py index a7d0aefc6d..a061dfc354 100644 --- a/api/core/workflow/nodes/iteration/iteration_node.py +++ b/api/core/workflow/nodes/iteration/iteration_node.py @@ -353,27 +353,26 @@ class IterationNode(BaseNode[IterationNodeData]): ) -> NodeRunStartedEvent | BaseNodeEvent | InNodeEvent: """ add iteration metadata to event. + ensures iteration context (ID, index/parallel_run_id) is added to metadata, """ if not isinstance(event, BaseNodeEvent): return event if self.node_data.is_parallel and isinstance(event, NodeRunStartedEvent): event.parallel_mode_run_id = parallel_mode_run_id - return event + + iter_metadata = { + NodeRunMetadataKey.ITERATION_ID: self.node_id, + NodeRunMetadataKey.ITERATION_INDEX: iter_run_index, + } + if parallel_mode_run_id: + # for parallel, the specific branch ID is more important than the sequential index + iter_metadata[NodeRunMetadataKey.PARALLEL_MODE_RUN_ID] = parallel_mode_run_id + if event.route_node_state.node_run_result: - metadata = event.route_node_state.node_run_result.metadata - if not metadata: - metadata = {} - if NodeRunMetadataKey.ITERATION_ID not in metadata: - metadata = { - **metadata, - NodeRunMetadataKey.ITERATION_ID: self.node_id, - NodeRunMetadataKey.PARALLEL_MODE_RUN_ID - if self.node_data.is_parallel - else NodeRunMetadataKey.ITERATION_INDEX: parallel_mode_run_id - if self.node_data.is_parallel - else iter_run_index, - } - event.route_node_state.node_run_result.metadata = metadata + current_metadata = event.route_node_state.node_run_result.metadata or {} + if NodeRunMetadataKey.ITERATION_ID not in current_metadata: + event.route_node_state.node_run_result.metadata = {**current_metadata, **iter_metadata} + return event def _run_single_iter( diff --git a/api/core/workflow/nodes/loop/loop_node.py b/api/core/workflow/nodes/loop/loop_node.py index eae33c0a92..bad3e2b928 100644 --- a/api/core/workflow/nodes/loop/loop_node.py +++ b/api/core/workflow/nodes/loop/loop_node.py @@ -337,7 +337,7 @@ class LoopNode(BaseNode[LoopNodeData]): return {"check_break_result": True} elif isinstance(event, NodeRunFailedEvent): # Loop run failed - yield event + yield self._handle_event_metadata(event=event, iter_run_index=current_index) yield LoopRunFailedEvent( loop_id=self.id, loop_node_id=self.node_id, diff --git a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx index ee051883f7..91bcaf9485 100644 --- a/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx +++ b/web/app/components/workflow/run/iteration-log/iteration-log-trigger.tsx @@ -9,44 +9,90 @@ import { Iteration } from '@/app/components/base/icons/src/vender/workflow' type IterationLogTriggerProps = { nodeInfo: NodeTracing + allExecutions?: NodeTracing[] onShowIterationResultList: (iterationResultList: NodeTracing[][], iterationResultDurationMap: IterationDurationMap) => void } const IterationLogTrigger = ({ nodeInfo, + allExecutions, onShowIterationResultList, }: IterationLogTriggerProps) => { const { t } = useTranslation() + + const filterNodesForInstance = (key: string): NodeTracing[] => { + if (!allExecutions) return [] + + const parallelNodes = allExecutions.filter(exec => + exec.execution_metadata?.parallel_mode_run_id === key, + ) + if (parallelNodes.length > 0) + return parallelNodes + + const serialIndex = parseInt(key, 10) + if (!isNaN(serialIndex)) { + const serialNodes = allExecutions.filter(exec => + exec.execution_metadata?.iteration_id === nodeInfo.node_id + && exec.execution_metadata?.iteration_index === serialIndex, + ) + if (serialNodes.length > 0) + return serialNodes + } + + return [] + } + + const handleOnShowIterationDetail = (e: React.MouseEvent) => { + e.stopPropagation() + e.nativeEvent.stopImmediatePropagation() + + const iterationNodeMeta = nodeInfo.execution_metadata + const iterDurationMap = nodeInfo?.iterDurationMap || iterationNodeMeta?.iteration_duration_map || {} + + let structuredList: NodeTracing[][] = [] + + if (iterationNodeMeta?.iteration_duration_map) { + const instanceKeys = Object.keys(iterationNodeMeta.iteration_duration_map) + structuredList = instanceKeys + .map(key => filterNodesForInstance(key)) + .filter(branchNodes => branchNodes.length > 0) + } + else if (nodeInfo.details?.length) { + structuredList = nodeInfo.details + } + + onShowIterationResultList(structuredList, iterDurationMap) + } + + let displayIterationCount = 0 + const iterMap = nodeInfo.execution_metadata?.iteration_duration_map + if (iterMap) + displayIterationCount = Object.keys(iterMap).length + else if (nodeInfo.details?.length) + displayIterationCount = nodeInfo.details.length + else if (nodeInfo.metadata?.iterator_length) + displayIterationCount = nodeInfo.metadata.iterator_length + const getErrorCount = (details: NodeTracing[][] | undefined) => { if (!details || details.length === 0) return 0 - return details.reduce((acc, iteration) => { if (iteration.some(item => item.status === 'failed')) acc++ return acc }, 0) } - const getCount = (iteration_curr_length: number | undefined, iteration_length: number) => { - if ((iteration_curr_length && iteration_curr_length < iteration_length) || !iteration_length) - return iteration_curr_length + const errorCount = getErrorCount(nodeInfo.details) - return iteration_length - } - const handleOnShowIterationDetail = (e: React.MouseEvent) => { - e.stopPropagation() - e.nativeEvent.stopImmediatePropagation() - onShowIterationResultList(nodeInfo.details || [], nodeInfo?.iterDurationMap || nodeInfo.execution_metadata?.iteration_duration_map || {}) - } return (