From 4eb847d38b23e0567ed158dbb8e51bde33019591 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Sat, 28 Jun 2025 15:13:38 +0800 Subject: [PATCH 01/18] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20=E8=AF=BB?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AE=89=E5=85=A8=E5=8A=A0=E5=9B=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/service/graph_service.py | 4 +- .../server/app/utils/graph_utils.py | 153 +++++++++++------- 2 files changed, 101 insertions(+), 56 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py index 70ef0b2172a..36c3aa15791 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py @@ -135,7 +135,7 @@ class GraphService: config['colors'] = config_data_run.get('colors') # 读取目录下配置文件列表 config_files = GraphUtils.find_config_files(run) - config['matchedConfigFiles'] = config_files + config['matchedConfigFiles'] = config_files or [] config['task'] = graph_data.get('task') return {'success': True, 'data': config} except Exception as e: @@ -329,7 +329,7 @@ class GraphService: config_data_run['colors'] = colors config_data[run] = config_data_run GraphState.set_global_value("config_data", config_data) - GraphUtils.safe_save_data(first_file_data, run, first_run_tag) + GraphUtils.safe_save_data(first_file_data, run, f"{first_run_tag}.vis") return {'success': True, 'error': None, 'data': {}} except Exception as e: return {'success': False, 'error': str(e), 'data': None} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py index 41a86a45bfb..3f1af3503ab 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py @@ -19,12 +19,14 @@ import os import json import re import stat +import sys from functools import cmp_to_key from pathlib import Path from tensorboard.util import tb_logging from .global_state import GraphState, FILE_NAME_REGEX, MAX_FILE_SIZE logger = tb_logging.get_logger() +FILE_PATH_MAX_LENGTH = 4096 class GraphUtils: @@ -210,6 +212,30 @@ class GraphUtils: return f"{size_bytes:.{decimal_places}f} {units[unit_index]}" + @staticmethod + def has_group_permission(path, permission): + """ + 检查指定路径是否对其所属组具有指定权限。 + + :param path: 要检查的文件或目录的路径。 + :param permission: 要检查的权限,使用 stat 中定义的常量,如 stat.S_IWGRP 表示写权限。 + :return: 如果所属组有指定权限,则返回 True;否则返回 False。 + """ + try: + # 获取文件的状态信息 + st = os.stat(path) + except FileNotFoundError: + return False + + # 使用位运算检查权限 + group_permissions = st.st_mode & (stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP) + + # 判断指定的权限是否存在 + if group_permissions & permission: + return True + else: + return False + @staticmethod def safe_save_data(data, run_name, tag): runs = GraphState.get_global_value('runs', {}) @@ -222,13 +248,13 @@ class GraphUtils: # 构建文件路径并标准化 file_path = os.path.join(run, tag) file_path = os.path.normpath(file_path) - # 权限校验:检查目录是否有写权限 - if not os.access(run, os.W_OK): - raise PermissionError(f"No write permission for directory: {run}\n") # 检查 run 目录是否存在,如果不存在则创建 if not os.path.exists(run): os.makedirs(run, exist_ok=True) os.chmod(run, 0o750) + # 权限校验:检查目录所属组是否有写权限 + if not os.stat(file_path).st_mode and not GraphUtils.has_group_permission(file_path, stat.stat.S_IWGRP): + raise PermissionError(f"No write permission for directory: {run}\n") # 检查 tag 是否为合法文件名 if not re.match(FILE_NAME_REGEX, tag): raise ValueError(f"Invalid tag: {tag}.") @@ -262,7 +288,6 @@ class GraphUtils: @staticmethod def safe_load_data(run_name, tag, only_check=False): - runs = GraphState.get_global_value('runs', {}) run_dir = runs.get(str(run_name)) or run_name """Load a single .vis file from a given directory based on the tag.""" @@ -271,33 +296,14 @@ class GraphUtils: return None, error_message try: file_path = os.path.join(run_dir, tag) - file_path = os.path.normpath(file_path) # 标准化路径 - # 解析真实路径(包含符号链接跟踪) - real_path = os.path.realpath(file_path) - safe_base_dir = GraphState.get_global_value('logdir') - # 安全验证1:路径归属检查(防止越界访问) - if not GraphUtils.is_relative_to(file_path, safe_base_dir): - raise RuntimeError(f"Path out of bounds:") - # 安全验证2:禁止符号链接文件 - if os.path.islink(file_path): - raise RuntimeError(f"Detected symbolic link file") - if os.path.islink(run_dir): - raise RuntimeError(f"Parent directory contains a symbolic link") - # 安全验证3:二次文件类型检查(防御TOCTOU攻击) - if not os.path.isfile(real_path): - raise RuntimeError(f"Path is not a regular file") - # 安全检查4:文件存在性验证 - if not os.path.exists(real_path): - raise FileNotFoundError(f"File does not exist") - # 权限验证 - if not os.stat(real_path).st_mode & stat.S_IRUSR: - raise PermissionError(f"File has no read permissions") - # 文件大小验证 - if os.path.getsize(real_path) > MAX_FILE_SIZE: - file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(real_path)) - max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) - raise RuntimeError( - f"File size exceeds limit ({file_size} > {max_size})") + # 目录安全校验 + success, error = GraphUtils.safe_check_load_file_path(run_dir, True) + if not success: + raise RuntimeError(error) + # 文件安全校验 + success, error = GraphUtils.safe_check_load_file_path(file_path) + if not success: + raise RuntimeError(error) # 读取文件比较耗时,支持onlyCheck参数,仅进行安全校验 if only_check: return True, None @@ -309,8 +315,64 @@ class GraphUtils: return None, "File is not a valid JSON file!" except Exception as e: logger.error(f'Error: File "{file_path}" is not accessible. Error: {e}') - return None, 'failed to load file' + return None, e + @staticmethod + def safe_check_load_file_path(file_path, isDir=False): + # 权限常量定义 + PERM_GROUP_WRITE = 0o020 + PERM_OTHER_WRITE = 0o002 + PERM_OTHER_READ = 0o004 + file_path = os.path.normpath(file_path) # 标准化路径 + real_path = os.path.realpath(file_path) + safe_base_dir = GraphState.get_global_value('logdir') + st = os.stat(real_path) + try: + # 安全验证:路径长度检查 + if len(real_path) > FILE_PATH_MAX_LENGTH: + raise RuntimeError(f"Path length exceeds limit") + # 安全验证:路径归属检查(防止越界访问) + if not GraphUtils.is_relative_to(file_path, safe_base_dir): + raise RuntimeError(f"Path out of bounds") + # 安全检查:文件存在性验证 + if not os.path.exists(real_path): + raise FileNotFoundError(f"File does not exist") + # 安全验证:禁止符号链接文件 + if os.path.islink(file_path): + raise RuntimeError(f"Detected symbolic link file") + # 安全验证:文件类型检查(防御TOCTOU攻击) + # 文件类型 + if not isDir and not os.path.isfile(real_path): + raise RuntimeError(f"Path is not a regular file") + # 目录类型 + if isDir and not Path(real_path).is_dir(): + raise RuntimeError(f"Directory does not exist") + # 可读性检查 + if not st.st_mode & PERM_OTHER_READ: + raise RuntimeError(f"Directory lacks read permission for others") + # 文件大小校验 + if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: + file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(file_path)) + max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) + raise RuntimeError( + f"File size exceeds limit ({file_size} > {max_size})") + # 非windows系统下,属主检查 + if os.name != 'nt': + current_uid = os.getuid() + # 如果是root用户,跳过后续权限检查 + if current_uid == 0: + return True + # 属主检查 + if st.st_uid != current_uid: + raise RuntimeError(f"Directory is not owned by the current user") + # group和其他用户不可写检查 + if st.st_mode & PERM_GROUP_WRITE or st.st_mode & PERM_OTHER_WRITE: + raise RuntimeError(f"Directory has group or other write permission") + return True, None + except Exception as e: + logger.error(e) + return False, e + @staticmethod def find_config_files(run_name): """ @@ -322,28 +384,11 @@ class GraphUtils: run = runs.get(run_name) dir_path = Path(run) try: - file_path = os.path.normpath(run) # 标准化路径 - # 解析真实路径(包含符号链接跟踪) - real_path = os.path.realpath(file_path) - safe_base_dir = GraphState.get_global_value('logdir') - # 安全验证1:路径归属检查(防止越界访问) - if not GraphUtils.is_relative_to(file_path, safe_base_dir): - raise RuntimeError(f"Path out of bounds:") - # 安全验证2:禁止符号链接文件 - if os.path.islink(file_path): - raise RuntimeError(f"Detected symbolic link file") - # 安全检查3:文件存在性验证 - if not os.path.exists(real_path): - raise FileNotFoundError(f"File does not exist") - # 权限验证 - if not os.stat(real_path).st_mode & stat.S_IRUSR: - raise PermissionError(f"File has no read permissions") - if not dir_path.is_dir(): - raise ValueError(f"The provided path '{run}' is not a valid directory.") - return [ - file.name for file in dir_path.iterdir() - if file.is_file() and file.name.endswith('.vis.config') - ] + if GraphUtils.safe_check_load_file_path(run, True): + return [ + file.name for file in dir_path.iterdir() + if file.is_file() and file.name.endswith('.vis.config') + ] except Exception as e: logger.error(e) return [] -- Gitee From dec6d1dc7a31cc4fa2d926c4a9097fb815f12960 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Sat, 28 Jun 2025 16:07:49 +0800 Subject: [PATCH 02/18] =?UTF-8?q?=F0=9F=A6=84=20refactor:=20=E5=86=99?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=AE=89=E5=85=A8=E6=A0=A1=E9=AA=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/utils/graph_utils.py | 132 ++++++++++-------- 1 file changed, 72 insertions(+), 60 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py index 3f1af3503ab..03cf11bc3af 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py @@ -212,63 +212,28 @@ class GraphUtils: return f"{size_bytes:.{decimal_places}f} {units[unit_index]}" - @staticmethod - def has_group_permission(path, permission): - """ - 检查指定路径是否对其所属组具有指定权限。 - - :param path: 要检查的文件或目录的路径。 - :param permission: 要检查的权限,使用 stat 中定义的常量,如 stat.S_IWGRP 表示写权限。 - :return: 如果所属组有指定权限,则返回 True;否则返回 False。 - """ - try: - # 获取文件的状态信息 - st = os.stat(path) - except FileNotFoundError: - return False - - # 使用位运算检查权限 - group_permissions = st.st_mode & (stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP) - - # 判断指定的权限是否存在 - if group_permissions & permission: - return True - else: - return False - @staticmethod def safe_save_data(data, run_name, tag): runs = GraphState.get_global_value('runs', {}) run = runs.get(run_name) or run_name - safe_base_dir = GraphState.get_global_value('logdir') if run is None or tag is None: error_message = 'The query parameters "run" and "tag" are required' return None, error_message try: - # 构建文件路径并标准化 - file_path = os.path.join(run, tag) - file_path = os.path.normpath(file_path) - # 检查 run 目录是否存在,如果不存在则创建 - if not os.path.exists(run): - os.makedirs(run, exist_ok=True) - os.chmod(run, 0o750) - # 权限校验:检查目录所属组是否有写权限 - if not os.stat(file_path).st_mode and not GraphUtils.has_group_permission(file_path, stat.stat.S_IWGRP): - raise PermissionError(f"No write permission for directory: {run}\n") # 检查 tag 是否为合法文件名 if not re.match(FILE_NAME_REGEX, tag): raise ValueError(f"Invalid tag: {tag}.") - # 检查文件路径是否合法,防止路径遍历攻击 - if not file_path.startswith(os.path.abspath(run)): - raise ValueError(f"Invalid file path: {file_path}. Potential path traversal attack.\n") - # 基础路径校验 - if not GraphUtils.is_relative_to(file_path, safe_base_dir): - raise ValueError(f"Path out of bounds: {file_path}") - if os.path.islink(file_path): - raise RuntimeError("The target file is a symbolic link") - if os.path.islink(run): - raise RuntimeError(f"Parent directory contains a symbolic link") - # # 尝试写入文件 + # 构建文件路径并标准化 + file_path = os.path.join(run, tag) + # 目录安全校验 + success, error = GraphUtils.safe_check_save_file_path(run, True) + if not success: + raise PermissionError(error) + # 文件安全校验 + success, error = GraphUtils.safe_check_save_file_path(file_path) + if not success: + raise PermissionError(error) + # 尝试写入文件 with open(file_path, "w", encoding="utf-8") as file: json.dump(data, file, ensure_ascii=False, indent=4) os.chmod(file_path, 0o640) @@ -281,7 +246,7 @@ class GraphUtils: return None, 'Invalid data' except OSError as e: logger.error(f"Failed to create directory: {run}. Error: {e}\n") - return None, 'failed to create directory {run}' + return None, 'failed to create directory ' except Exception as e: logger.error(f'Error: File "{file_path}" is not accessible. Error: {e}') return None, 'failed to save file' @@ -299,11 +264,11 @@ class GraphUtils: # 目录安全校验 success, error = GraphUtils.safe_check_load_file_path(run_dir, True) if not success: - raise RuntimeError(error) + raise PermissionError(error) # 文件安全校验 success, error = GraphUtils.safe_check_load_file_path(file_path) if not success: - raise RuntimeError(error) + raise PermissionError(error) # 读取文件比较耗时,支持onlyCheck参数,仅进行安全校验 if only_check: return True, None @@ -317,12 +282,59 @@ class GraphUtils: logger.error(f'Error: File "{file_path}" is not accessible. Error: {e}') return None, e + @staticmethod + def safe_check_save_file_path(file_path, isDir=False): + PERM_GROUP_WRITE = 0o020 + PERM_OTHER_WRITE = 0o002 + st = os.stat(file_path) + file_path = os.path.normpath(file_path) # 标准化路径 + real_path = os.path.realpath(file_path) + safe_base_dir = GraphState.get_global_value('logdir') + try: + # 安全验证:路径长度检查 + if len(file_path) > FILE_PATH_MAX_LENGTH: + raise PermissionError(f"Path length exceeds limit") + # 安全验证:基础路径校验 + if not GraphUtils.is_relative_to(file_path, safe_base_dir): + raise ValueError(f"Path out of bounds: {file_path}") + # 安全验证:禁止符号链接文件 + if os.path.islink(file_path): + raise RuntimeError("The target file is a symbolic link") + # 安全验证:文件大小校验 + if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: + file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(file_path)) + max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) + raise PermissionError( + f"File size exceeds limit ({file_size} > {max_size})") + # 安全验证:检查目录是否存在,如果不存在则创建 + if isDir and not os.path.exists(real_path): + os.makedirs(real_path, exist_ok=True) + os.chmod(file_path, 0o640) + # 权限校验:检查是否有写权限 + if not os.stat(file_path).st_mode & stat.S_IWGRP: + raise PermissionError(f"No write permission for directory\n") + # 安全验证: 非windows系统下,属主检查 + if os.name != 'nt': + current_uid = os.getuid() + # 如果是root用户,跳过后续权限检查 + if current_uid == 0: + return True + # 属主检查 + if st.st_uid != current_uid: + raise PermissionError(f"Directory is not owned by the current user") + # group和其他用户不可写检查 + if st.st_mode & PERM_GROUP_WRITE or st.st_mode & PERM_OTHER_WRITE: + raise PermissionError(f"Directory has group or other write permission") + return True, None + except Exception as e: + logger.error(e) + return False, e + @staticmethod def safe_check_load_file_path(file_path, isDir=False): # 权限常量定义 PERM_GROUP_WRITE = 0o020 PERM_OTHER_WRITE = 0o002 - PERM_OTHER_READ = 0o004 file_path = os.path.normpath(file_path) # 标准化路径 real_path = os.path.realpath(file_path) safe_base_dir = GraphState.get_global_value('logdir') @@ -330,31 +342,31 @@ class GraphUtils: try: # 安全验证:路径长度检查 if len(real_path) > FILE_PATH_MAX_LENGTH: - raise RuntimeError(f"Path length exceeds limit") + raise PermissionError(f"Path length exceeds limit") # 安全验证:路径归属检查(防止越界访问) if not GraphUtils.is_relative_to(file_path, safe_base_dir): - raise RuntimeError(f"Path out of bounds") + raise PermissionError(f"Path out of bounds") # 安全检查:文件存在性验证 if not os.path.exists(real_path): raise FileNotFoundError(f"File does not exist") # 安全验证:禁止符号链接文件 if os.path.islink(file_path): - raise RuntimeError(f"Detected symbolic link file") + raise PermissionError(f"Detected symbolic link file") # 安全验证:文件类型检查(防御TOCTOU攻击) # 文件类型 if not isDir and not os.path.isfile(real_path): - raise RuntimeError(f"Path is not a regular file") + raise PermissionError(f"Path is not a regular file") # 目录类型 if isDir and not Path(real_path).is_dir(): - raise RuntimeError(f"Directory does not exist") + raise PermissionError(f"Directory does not exist") # 可读性检查 - if not st.st_mode & PERM_OTHER_READ: - raise RuntimeError(f"Directory lacks read permission for others") + if not st.st_mode & stat.S_IRGRP: + raise PermissionError(f"Directory lacks read permission for others") # 文件大小校验 if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(file_path)) max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) - raise RuntimeError( + raise PermissionError( f"File size exceeds limit ({file_size} > {max_size})") # 非windows系统下,属主检查 if os.name != 'nt': @@ -364,10 +376,10 @@ class GraphUtils: return True # 属主检查 if st.st_uid != current_uid: - raise RuntimeError(f"Directory is not owned by the current user") + raise PermissionError(f"Directory is not owned by the current user") # group和其他用户不可写检查 if st.st_mode & PERM_GROUP_WRITE or st.st_mode & PERM_OTHER_WRITE: - raise RuntimeError(f"Directory has group or other write permission") + raise PermissionError(f"Directory has group or other write permission") return True, None except Exception as e: logger.error(e) -- Gitee From e9e0e3463374db4c790832e582630ec984efcafe Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Sat, 28 Jun 2025 17:51:48 +0800 Subject: [PATCH 03/18] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=E5=86=99=E5=85=A5?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/utils/graph_utils.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py index 03cf11bc3af..45dea610598 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py @@ -286,7 +286,6 @@ class GraphUtils: def safe_check_save_file_path(file_path, isDir=False): PERM_GROUP_WRITE = 0o020 PERM_OTHER_WRITE = 0o002 - st = os.stat(file_path) file_path = os.path.normpath(file_path) # 标准化路径 real_path = os.path.realpath(file_path) safe_base_dir = GraphState.get_global_value('logdir') @@ -297,21 +296,18 @@ class GraphUtils: # 安全验证:基础路径校验 if not GraphUtils.is_relative_to(file_path, safe_base_dir): raise ValueError(f"Path out of bounds: {file_path}") + if not os.path.exists(file_path): + return True, None + st = os.stat(file_path) # 安全验证:禁止符号链接文件 if os.path.islink(file_path): - raise RuntimeError("The target file is a symbolic link") - # 安全验证:文件大小校验 - if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: - file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(file_path)) - max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) - raise PermissionError( - f"File size exceeds limit ({file_size} > {max_size})") + raise PermissionError("The target file is a symbolic link") # 安全验证:检查目录是否存在,如果不存在则创建 if isDir and not os.path.exists(real_path): os.makedirs(real_path, exist_ok=True) os.chmod(file_path, 0o640) # 权限校验:检查是否有写权限 - if not os.stat(file_path).st_mode & stat.S_IWGRP: + if not os.stat(file_path).st_mode & stat.S_IWUSR: raise PermissionError(f"No write permission for directory\n") # 安全验证: 非windows系统下,属主检查 if os.name != 'nt': @@ -360,7 +356,7 @@ class GraphUtils: if isDir and not Path(real_path).is_dir(): raise PermissionError(f"Directory does not exist") # 可读性检查 - if not st.st_mode & stat.S_IRGRP: + if not st.st_mode & stat.S_IRUSR: raise PermissionError(f"Directory lacks read permission for others") # 文件大小校验 if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: -- Gitee From 8c8760ce2f80b583575bed337f65c858374a3c8d Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Mon, 30 Jun 2025 10:02:20 +0800 Subject: [PATCH 04/18] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=20=E6=B3=A8?= =?UTF-8?q?=E9=87=8A=E8=AF=AF=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tb_graph_ascend/server/app/controllers/hierarchy.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py index b5870f29885..acb60aeae93 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py @@ -53,15 +53,11 @@ class Hierarchy: def extract_label_name(node_name, node_type): splited_subnode_name = node_name.split('.') splited_label = [] - # Module.layer1.1.relu.ReLU.forward.1 =>relu.ReLU.forward.1 - # Module.layer1.1.relu.ReLU.forward.1 =>relu.ReLU.forward.1 if node_type == MODULE: if len(splited_subnode_name) < 4: return node_name splited_label = splited_subnode_name[-4:] if not splited_subnode_name[ -4].isdigit() else splited_subnode_name[-5:] - # Module.layer1.1.ApiList.1 =>ApiList.1 - # Module.layer1.1.ApiList.0.1 =>ApiList.0.1 else: if len(splited_subnode_name) < 2: return node_name -- Gitee From ec08b06847f0e6586eadfc851735a04b3a6a7661 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Mon, 30 Jun 2025 11:03:36 +0800 Subject: [PATCH 05/18] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=B8=8D=E5=AD=98=E5=9C=A8=E4=B8=8D=E5=9C=A8=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E4=B8=8B=E5=B1=9E=E5=AE=89=E5=85=A8=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tb_graph_ascend/server/app/controllers/hierarchy.py | 6 ++++++ .../tb_graph_ascend/server/app/utils/graph_utils.py | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py index acb60aeae93..1a839320021 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/hierarchy.py @@ -53,11 +53,17 @@ class Hierarchy: def extract_label_name(node_name, node_type): splited_subnode_name = node_name.split('.') splited_label = [] + # 在展开层级时,将父级层级名称相关去除,仅保留子节点本身名称信息 + # 如Module.layer1.1.relu.ReLU.forward.0中的父级名称Module.layer1.1去除,仅保留子级的relu.ReLU.forward.1 + # 如Module.layer4.0.BasicBlock.forward.0中的父级名称Module.1去除,仅保留子级的layer4.0.BasicBlock.forward.0 if node_type == MODULE: if len(splited_subnode_name) < 4: return node_name splited_label = splited_subnode_name[-4:] if not splited_subnode_name[ -4].isdigit() else splited_subnode_name[-5:] + # 在展开层级时,将父级层级名称相关去除,仅保留API子节点本身名称信息, + # 如 Module.layer1.1.ApiList.1 中的父级名称Module.layer1.1去除,仅保留子级的ApiList.1 + # 如 Module.layer1.1.ApiList.0.1 中的父级名称Module.layer1.1去除,仅保留子级的ApiList.0.1 else: if len(splited_subnode_name) < 2: return node_name diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py index 45dea610598..89b6f3f829d 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py @@ -296,7 +296,7 @@ class GraphUtils: # 安全验证:基础路径校验 if not GraphUtils.is_relative_to(file_path, safe_base_dir): raise ValueError(f"Path out of bounds: {file_path}") - if not os.path.exists(file_path): + if not isDir and not os.path.exists(file_path): return True, None st = os.stat(file_path) # 安全验证:禁止符号链接文件 -- Gitee From ea491a1a95e80fbfb249a830885cfcf9658d5e87 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Mon, 30 Jun 2025 15:50:17 +0800 Subject: [PATCH 06/18] =?UTF-8?q?=F0=9F=90=9E=20fix:=20code=20check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/utils/global_state.py | 3 +++ .../server/app/utils/graph_utils.py | 24 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/global_state.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/global_state.py index 979e5b4f340..6e4fcabaeef 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/global_state.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/global_state.py @@ -36,6 +36,9 @@ SINGLE = 'Single' # 前端节点类型 EXPAND_MODULE = 0 UNEXPAND_NODE = 1 +# 权限码 +PERM_GROUP_WRITE = 0o020 +PERM_OTHER_WRITE = 0o002 # 后端节点类型 MODULE = 0 diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py index 89b6f3f829d..b5efe347bb4 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/utils/graph_utils.py @@ -23,7 +23,7 @@ import sys from functools import cmp_to_key from pathlib import Path from tensorboard.util import tb_logging -from .global_state import GraphState, FILE_NAME_REGEX, MAX_FILE_SIZE +from .global_state import GraphState, FILE_NAME_REGEX, MAX_FILE_SIZE, PERM_GROUP_WRITE, PERM_OTHER_WRITE logger = tb_logging.get_logger() FILE_PATH_MAX_LENGTH = 4096 @@ -283,9 +283,7 @@ class GraphUtils: return None, e @staticmethod - def safe_check_save_file_path(file_path, isDir=False): - PERM_GROUP_WRITE = 0o020 - PERM_OTHER_WRITE = 0o002 + def safe_check_save_file_path(file_path, is_dir=False): file_path = os.path.normpath(file_path) # 标准化路径 real_path = os.path.realpath(file_path) safe_base_dir = GraphState.get_global_value('logdir') @@ -296,14 +294,14 @@ class GraphUtils: # 安全验证:基础路径校验 if not GraphUtils.is_relative_to(file_path, safe_base_dir): raise ValueError(f"Path out of bounds: {file_path}") - if not isDir and not os.path.exists(file_path): - return True, None + if not is_dir and not os.path.exists(file_path): + return True, None st = os.stat(file_path) # 安全验证:禁止符号链接文件 if os.path.islink(file_path): raise PermissionError("The target file is a symbolic link") # 安全验证:检查目录是否存在,如果不存在则创建 - if isDir and not os.path.exists(real_path): + if is_dir and not os.path.exists(real_path): os.makedirs(real_path, exist_ok=True) os.chmod(file_path, 0o640) # 权限校验:检查是否有写权限 @@ -327,10 +325,8 @@ class GraphUtils: return False, e @staticmethod - def safe_check_load_file_path(file_path, isDir=False): + def safe_check_load_file_path(file_path, is_dir=False): # 权限常量定义 - PERM_GROUP_WRITE = 0o020 - PERM_OTHER_WRITE = 0o002 file_path = os.path.normpath(file_path) # 标准化路径 real_path = os.path.realpath(file_path) safe_base_dir = GraphState.get_global_value('logdir') @@ -350,16 +346,16 @@ class GraphUtils: raise PermissionError(f"Detected symbolic link file") # 安全验证:文件类型检查(防御TOCTOU攻击) # 文件类型 - if not isDir and not os.path.isfile(real_path): + if not is_dir and not os.path.isfile(real_path): raise PermissionError(f"Path is not a regular file") # 目录类型 - if isDir and not Path(real_path).is_dir(): + if is_dir and not Path(real_path).is_dir(): raise PermissionError(f"Directory does not exist") # 可读性检查 if not st.st_mode & stat.S_IRUSR: raise PermissionError(f"Directory lacks read permission for others") # 文件大小校验 - if not isDir and os.path.getsize(file_path) > MAX_FILE_SIZE: + if not is_dir and os.path.getsize(file_path) > MAX_FILE_SIZE: file_size = GraphUtils.bytes_to_human_readable(os.path.getsize(file_path)) max_size = GraphUtils.bytes_to_human_readable(MAX_FILE_SIZE) raise PermissionError( @@ -397,6 +393,8 @@ class GraphUtils: file.name for file in dir_path.iterdir() if file.is_file() and file.name.endswith('.vis.config') ] + else: + return [] except Exception as e: logger.error(e) return [] -- Gitee From bd9e18ca32c3cb00577c215e77b47f1ce197cc26 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Tue, 1 Jul 2025 15:06:47 +0800 Subject: [PATCH 07/18] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=E8=87=AA=E5=8A=A8?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E8=8A=82=E7=82=B9=E6=94=AF=E6=8C=81=E5=8F=96?= =?UTF-8?q?=E6=B6=88=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../server/app/controllers/match_nodes_controller.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/match_nodes_controller.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/match_nodes_controller.py index 8704a93e9bb..e2e500c1334 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/match_nodes_controller.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/controllers/match_nodes_controller.py @@ -270,8 +270,10 @@ class MatchNodesController: @staticmethod def process_md5_task_delete(graph_data, npu_node_name, bench_node_name): config_data = GraphState.get_global_value("config_data") - manual_match_nodes = config_data.get('manualMatchNodes', {}) - if manual_match_nodes.get(npu_node_name) != bench_node_name: + npu_match_nodes_list = config_data.get('npuMatchNodes', {}) + bench_match_nodes_list = config_data.get('benchMatchNodes', {}) + if npu_match_nodes_list.get(npu_node_name) != bench_node_name or bench_match_nodes_list.get( + bench_node_name) != npu_node_name: return { 'success': False, 'error': "操作失败:节点未匹配,请先匹配节点", @@ -292,8 +294,10 @@ class MatchNodesController: @staticmethod def process_summary_task_delete(graph_data, npu_node_name, bench_node_name): config_data = GraphState.get_global_value("config_data") - manual_match_nodes = config_data.get('manualMatchNodes', {}) - if manual_match_nodes.get(npu_node_name) != bench_node_name: + npu_match_nodes_list = config_data.get('npuMatchNodes', {}) + bench_match_nodes_list = config_data.get('benchMatchNodes', {}) + if npu_match_nodes_list.get(npu_node_name) != bench_node_name or bench_match_nodes_list.get( + bench_node_name) != npu_node_name: return { 'success': False, 'error': "操作失败:节点未匹配,请先匹配节点", -- Gitee From 0d726d0917f78bb3619c2e86c4f1b66cd0883201 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Tue, 1 Jul 2025 16:01:36 +0800 Subject: [PATCH 08/18] =?UTF-8?q?=E2=9C=A8=20feat:=20=E6=96=87=E6=9C=AC?= =?UTF-8?q?=E6=A1=86=E9=AB=98=E5=BA=A6=E5=A2=9E=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/tf_vaddin_text_table/index.ts | 2 +- .../fe/src/graph_info_board/useNodeInfo.ts | 14 -------------- 2 files changed, 1 insertion(+), 15 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/components/tf_vaddin_text_table/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/components/tf_vaddin_text_table/index.ts index 9f6d663fc96..7e97498e6c4 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/components/tf_vaddin_text_table/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/components/tf_vaddin_text_table/index.ts @@ -39,7 +39,7 @@ class TfVaadinTable extends PolymerElement { .copyable-input { gap: 8px; width: 100%; - height: 160px; + height: 260px; font-family: Roboto, sans-serif; font-weight: 400; } diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/useNodeInfo.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/useNodeInfo.ts index 9ea8877b2e8..d9557cec429 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/useNodeInfo.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_info_board/useNodeInfo.ts @@ -167,20 +167,6 @@ const useNodeInfo = (): UseNodeInfoType => { stackInfo[title] = 'stackInfo'; detailData.push(stackInfo); } - // 获取suggestions - const suggestion: Record = {}; - const npusuggestion = npuNode?.suggestions; - const benchsuggestion = benchNode?.suggestions; - if (!isEmpty(npusuggestion)) { - suggestion[nodeName] = converObjectToString(npusuggestion); - } - if (!isEmpty(benchsuggestion)) { - suggestion[benchNodeName] = converObjectToString(benchsuggestion); - } - if (!isEmpty(suggestion)) { - suggestion[title] = 'suggestions'; - detailData.push(suggestion); - } // 获取parallel_merge_info const parallelMergeInfo: Record = {}; const npuparallelMergeInfo = npuNode?.parallelMergeInfo; -- Gitee From 7d5e39bdd93ed9c5234b51f3be13416185a3e9e1 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Tue, 1 Jul 2025 17:29:04 +0800 Subject: [PATCH 09/18] =?UTF-8?q?=E2=9C=A8=20feat:=20=E5=8F=96=E6=B6=88?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E5=A2=9E=E5=8A=A0=E5=8F=AF=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tb_graph_ascend/fe/package-lock.json | 204 ++++++++++++++---- .../tb_graph_ascend/fe/package.json | 1 + .../components/tf_manual_match/index.ts | 18 +- .../components/tf_manual_match/useMatched.ts | 13 +- .../src/graph_controls_board/type/index.d.ts | 4 +- .../server/app/service/graph_service.py | 37 +++- .../server/app/views/graph_views.py | 6 +- 7 files changed, 225 insertions(+), 58 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json index 15f1931fe9b..0e2e1896ed6 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json @@ -29,6 +29,7 @@ "@polymer/polymer": "^3.5.1", "@types/lodash": "^4.17.1", "@vaadin/button": "24.6.5", + "@vaadin/checkbox": "^24.8.2", "@vaadin/combo-box": "24.6.5", "@vaadin/context-menu": "24.6.5", "@vaadin/details": "24.6.5", @@ -1217,14 +1218,14 @@ } }, "node_modules/@vaadin/a11y-base": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/a11y-base/-/a11y-base-24.6.7.tgz", - "integrity": "sha512-CJYYTWPBEEaVt4AvBE8RzEn3hqUZbGUGLzqs6NGBFTw0c5cfkqoO2ZMkKhz5Z52QF+2mCXpEtyg6s+t0h171Qg==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/a11y-base/-/a11y-base-24.6.11.tgz", + "integrity": "sha512-yBZ0QGPngbItIJQx3FRIa9IXDW2Ftf6SFFPGhbdAZafJPBlFi6FElP9cVtL3qjJlI5KKBp/UXEcC8ehPK207gw==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", "@polymer/polymer": "^3.0.0", - "@vaadin/component-base": "~24.6.7", + "@vaadin/component-base": "~24.6.11", "lit": "^3.0.0" } }, @@ -1245,22 +1246,108 @@ } }, "node_modules/@vaadin/checkbox": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/checkbox/-/checkbox-24.6.7.tgz", - "integrity": "sha512-/Vl5codokNdN5ku1l/iAkdjUmYTUZGKyAleHjM7V3ZFpwkK2IoWN4HrbWyhPuf1gL3T85bKMLSPuYoOX/ymrFw==", + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/checkbox/-/checkbox-24.8.2.tgz", + "integrity": "sha512-sQPrahkVyK1Mm0t5tGKLzkKLnkhnLgBOQTnCUNQDThyhldcAFmzModnzkl/6rf8dFz0ti35AwUaV0mebfAUsUw==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", "@polymer/polymer": "^3.0.0", - "@vaadin/a11y-base": "~24.6.7", - "@vaadin/component-base": "~24.6.7", - "@vaadin/field-base": "~24.6.7", - "@vaadin/vaadin-lumo-styles": "~24.6.7", - "@vaadin/vaadin-material-styles": "~24.6.7", - "@vaadin/vaadin-themable-mixin": "~24.6.7", + "@vaadin/a11y-base": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "@vaadin/field-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/a11y-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/a11y-base/-/a11y-base-24.8.2.tgz", + "integrity": "sha512-Cp2raqWoe/sgzuvpSo17ROxEvTMj+3gYfW8uAkCEC2bVmV542oMiPzLPyC0GP2GjJGzrvz7y9eQ3sq1ATZxLkA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/component-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/component-base/-/component-base-24.8.2.tgz", + "integrity": "sha512-pzM49fDKm4erK5GDIvakb0utCm8Xm7oBqkxyGJfgS3y9w8MGvJ6foUL/QD+13nW5zL6toZ+kwdIw7PvIF96pKQ==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/vaadin-development-mode-detector": "^2.0.0", + "@vaadin/vaadin-usage-statistics": "^2.1.0", "lit": "^3.0.0" } }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/field-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/field-base/-/field-base-24.8.2.tgz", + "integrity": "sha512-a74pBeaPI6E/bvwLCOlF8LyU0TcOqen1KSbERifPsPECRQq0ODif2pfOEiEatD1P/DM0jh8fdaiPjq2BTocNbw==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/a11y-base": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/icon": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.8.2.tgz", + "integrity": "sha512-0IhbiikTDJzpn9NQplKIIW2lK5tK6q2pLHXT+O41KA2ZcwTWoyAoKQyjU40vpHK+9EOGhwqX7LncugnWSO6iwg==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/vaadin-lumo-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-24.8.2.tgz", + "integrity": "sha512-DanIV4ZHjmb/1dF+oyj33IZVxEXLa1Sjb3ZDWrTlu6I59/veDsFE7g6y6ZYjNLE/O8YlE1oz8yHEUViSXmBwfw==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/icon": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/vaadin-material-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-24.8.2.tgz", + "integrity": "sha512-l0WMqPeH0bfoviZqu96h9CFZ92Y7nQwG9gb1mr9tkXd7XCbt/DEmCcvVaAJSpxVp+Qi7/J8z8Wn9855GExTdHg==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/checkbox/node_modules/@vaadin/vaadin-themable-mixin": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-24.8.2.tgz", + "integrity": "sha512-i0MLfbGU+B1fWEPxAQzrQOdQ2JfrYLyagOfe8FOKFAMm03xURtbD7hZKxFchULudphD+/NvHh2i++Vl3mGMfsA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "lit": "^3.0.0", + "style-observer": "^0.0.8" + } + }, "node_modules/@vaadin/combo-box": { "version": "24.6.5", "resolved": "https://registry.npmmirror.com/@vaadin/combo-box/-/combo-box-24.6.5.tgz", @@ -1283,9 +1370,9 @@ } }, "node_modules/@vaadin/component-base": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/component-base/-/component-base-24.6.7.tgz", - "integrity": "sha512-LcZQZEwouPDHBoXfXRREb1mRScsPSPeKTUZdgrXh180Piy57VzpNzslIMrdfVFSye9lLMs2/g2o8HCUDgnY/OQ==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/component-base/-/component-base-24.6.11.tgz", + "integrity": "sha512-7jR6vcJeCBgY2CNbAPLOcUTsxYspqdkA0slUGk3GwfgsRDD5FLkzqQDSM5+yE6O2+4Wah2Tk+kG/GsKGtlUlwg==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", @@ -1333,15 +1420,15 @@ } }, "node_modules/@vaadin/field-base": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/field-base/-/field-base-24.6.7.tgz", - "integrity": "sha512-5MXpAQGZA15/hRdnZrJK5q5Mv8rgOraSyBpC/gjRJ1W1IQ5DrCcb3ltvPATguv0K3vpJwunXGXrGqm/+SGEk0w==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/field-base/-/field-base-24.6.11.tgz", + "integrity": "sha512-dRjxKzbW3xQAau1xuO8uZepVWaImS2wEyKDK9Oh+y8iiu4smYEmo9e4aqMqQN/sOHU6OSa4YtbyJZlvD1sBXrA==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", "@polymer/polymer": "^3.0.0", - "@vaadin/a11y-base": "~24.6.7", - "@vaadin/component-base": "~24.6.7", + "@vaadin/a11y-base": "~24.6.11", + "@vaadin/component-base": "~24.6.11", "lit": "^3.0.0" } }, @@ -1364,6 +1451,23 @@ "lit": "^3.0.0" } }, + "node_modules/@vaadin/grid/node_modules/@vaadin/checkbox": { + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/checkbox/-/checkbox-24.6.11.tgz", + "integrity": "sha512-Uvd6gZ3xQQrZTtCJL6f4uLbg6mXsAKjiZto7Je39yJwUHz8r5MIQr+4mLF4zc6mYVSH/Ihj/a4n9FOuTwSEuQw==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/a11y-base": "~24.6.11", + "@vaadin/component-base": "~24.6.11", + "@vaadin/field-base": "~24.6.11", + "@vaadin/vaadin-lumo-styles": "~24.6.11", + "@vaadin/vaadin-material-styles": "~24.6.11", + "@vaadin/vaadin-themable-mixin": "~24.6.11", + "lit": "^3.0.0" + } + }, "node_modules/@vaadin/icon": { "version": "24.6.5", "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.6.5.tgz", @@ -1625,46 +1729,46 @@ "license": "Apache-2.0" }, "node_modules/@vaadin/vaadin-lumo-styles": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-24.6.7.tgz", - "integrity": "sha512-DNamU8cVxbaVn3HfRm3pN8ul95xvaem92ByVeEQwdvKaHwLI4m7AdSWKEA+13ST9TdBtCeDW6DjmtGcoEqbqiw==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-24.6.11.tgz", + "integrity": "sha512-WRluczao8lZgImdtl66v09YjFULb1iLAhcU48aiR9igAT7h6aLeHYBvRH3AA/gBlUNwHd4xlBSl89p4HP2GGog==", "license": "Apache-2.0", "dependencies": { "@polymer/polymer": "^3.0.0", - "@vaadin/component-base": "~24.6.7", - "@vaadin/icon": "~24.6.7", - "@vaadin/vaadin-themable-mixin": "~24.6.7" + "@vaadin/component-base": "~24.6.11", + "@vaadin/icon": "~24.6.11", + "@vaadin/vaadin-themable-mixin": "~24.6.11" } }, "node_modules/@vaadin/vaadin-lumo-styles/node_modules/@vaadin/icon": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.6.7.tgz", - "integrity": "sha512-+Cv3hLyFSXJAhnuGuPQ+hQcv9/ijZpIprJ6rqWeChvFk+bQOoPgUPx/tj67mOiTcrmV5hYt+dYs4QM7JZ//dGg==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.6.11.tgz", + "integrity": "sha512-CKOh+I84+GZRfMHrhtATtrw3bSW5eUArgGT4cKsOY3asoCZXUdTObPD/PqKfP4e2uAA1bgLl27kOc+W8dmibJA==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", "@polymer/polymer": "^3.0.0", - "@vaadin/component-base": "~24.6.7", - "@vaadin/vaadin-lumo-styles": "~24.6.7", - "@vaadin/vaadin-themable-mixin": "~24.6.7", + "@vaadin/component-base": "~24.6.11", + "@vaadin/vaadin-lumo-styles": "~24.6.11", + "@vaadin/vaadin-themable-mixin": "~24.6.11", "lit": "^3.0.0" } }, "node_modules/@vaadin/vaadin-material-styles": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-24.6.7.tgz", - "integrity": "sha512-7ecHOEZrFEbUz5UVSGapOt/uC7lSYV05RADCNhG16c+WsuN+oxkGIIaThMMCdBcclg5ej/BeTxZlZha8JoNO3g==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-24.6.11.tgz", + "integrity": "sha512-tDumwlaDp/s9u++MPi64I1o2ls/drWOZf4xVPhztUjt3NwYJUeVXtwu39q0wBRIeRM7UBrs06kug2CVT72U4qQ==", "license": "Apache-2.0", "dependencies": { "@polymer/polymer": "^3.0.0", - "@vaadin/component-base": "~24.6.7", - "@vaadin/vaadin-themable-mixin": "~24.6.7" + "@vaadin/component-base": "~24.6.11", + "@vaadin/vaadin-themable-mixin": "~24.6.11" } }, "node_modules/@vaadin/vaadin-themable-mixin": { - "version": "24.6.7", - "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-24.6.7.tgz", - "integrity": "sha512-fiVBvJWInNBq/oXeE0UAQmzadQ7UJE3ns768D1taKOwTMOxiio1UMoUXcVGwni9ASzXrd96S7F6c4aIaVqNx6A==", + "version": "24.6.11", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-24.6.11.tgz", + "integrity": "sha512-xCmn3X+2C7nI9LQn2OqLLkLw7VeJOCo99DlHwnxeLZpJJ/s8bjDXcIWflS+IOChzHixgEFkDSoLcNYoCR1RvYg==", "license": "Apache-2.0", "dependencies": { "@open-wc/dedupe-mixin": "^1.3.0", @@ -5977,6 +6081,22 @@ "webpack": "^5.27.0" } }, + "node_modules/style-observer": { + "version": "0.0.8", + "resolved": "https://registry.npmmirror.com/style-observer/-/style-observer-0.0.8.tgz", + "integrity": "sha512-UaIPn33Sx4BJ+goia51Q++VFWoplWK1995VdxQYzwwbFa+FUNLKlG+aiIdG2Vw7VyzIUBi8tqu8mTyg0Ppu6Yg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/LeaVerou" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/leaverou" + } + ], + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", @@ -6569,4 +6689,4 @@ } } } -} \ No newline at end of file +} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json index d5bf7321825..6bb4470b360 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json @@ -50,6 +50,7 @@ "@polymer/polymer": "^3.5.1", "@types/lodash": "^4.17.1", "@vaadin/button": "24.6.5", + "@vaadin/checkbox": "^24.8.2", "@vaadin/combo-box": "24.6.5", "@vaadin/context-menu": "24.6.5", "@vaadin/details": "24.6.5", diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/index.ts index 72d6ca4a833..9f6b98968d0 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/index.ts @@ -19,6 +19,7 @@ import '@vaadin/details'; import '@vaadin/combo-box'; import '@vaadin/tooltip'; import '@vaadin/progress-bar'; +import '@vaadin/checkbox'; import { isEmpty } from 'lodash'; import { Notification } from '@vaadin/notification'; import { PolymerElement, html } from '@polymer/polymer'; @@ -63,7 +64,12 @@ class Legend extends PolymerElement { tf-search-combox.matched-node::part(arraw-button) { margin-top: 28px !important; } - + .match-checkbox{ + font-size: 14px; + } + vaadin-checkbox::part(checkbox) { + + } .vaadin-details-title { font-size: 14px; color: #333333; @@ -160,6 +166,7 @@ class Legend extends PolymerElement { +
点击匹配
@@ -183,6 +190,7 @@ class Legend extends PolymerElement { +
取消匹配
@@ -285,6 +293,12 @@ class Legend extends PolymerElement { @property({ type: String }) selectedConfigFile: string = ''; + @property({ type: Boolean }) + isMatchChildren: boolean = true; + + @property({ type: Boolean }) + isUnMatchChildren: boolean = true; + useMatched: UseMatchedType = useMatched(); npuMatchedNodeList = {}; benchMatchedNodeList = {}; @@ -414,6 +428,7 @@ class Legend extends PolymerElement { this.selectedNpuMatchedNode, this.selectedBenchMatchedNode, this.selection, + this.isUnMatchChildren ); this.set('unmatchLoading', false); if (success) { @@ -534,6 +549,7 @@ class Legend extends PolymerElement { this.selectedNpuUnMatchedNode, this.selectedBenchUnMatchedNode, this.selection, + this.isMatchChildren, ); this.set('matchLoading', false); if (success) { diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/useMatched.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/useMatched.ts index efceeb4bc88..3358e257a5f 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/useMatched.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/components/tf_manual_match/useMatched.ts @@ -20,21 +20,23 @@ import { safeJSONParse } from '../../../utils'; import request from '../../../utils/request'; import { UseMatchedType, MatchResultType } from '../../type'; const useMatched = (): UseMatchedType => { - const requestAddMatchNodes = async (npuNodeName: string, benchNodeName: string, metaData: any): Promise => { + const requestAddMatchNodes = async (npuNodeName: string, benchNodeName: string, metaData: any, isMatchChildren: boolean): Promise => { const params = { npuNodeName: npuNodeName, benchNodeName: benchNodeName, metaData: JSON.stringify(metaData), + isMatchChildren }; const mactchResult = await request({ url: 'addMatchNodes', method: 'GET', params: params }); return mactchResult; }; - const requestDeleteMatchNodes = async (npuNodeName: string, benchNodeName: string, metaData: any): Promise => { + const requestDeleteMatchNodes = async (npuNodeName: string, benchNodeName: string, metaData: any, isUnMatchChildren: boolean): Promise => { const params = { npuNodeName: npuNodeName, benchNodeName: benchNodeName, metaData: JSON.stringify(metaData), + isUnMatchChildren }; const mactchResult = await request({ url: 'deleteMatchNodes', method: 'GET', params: params }); return mactchResult; @@ -89,6 +91,7 @@ const useMatched = (): UseMatchedType => { npuNodeName: string, benchNodeName: string, selection: any, + isMatchChildren: boolean ): Promise => { if (isEmpty(npuNodeName) || isEmpty(benchNodeName)) { return { @@ -100,11 +103,11 @@ const useMatched = (): UseMatchedType => { run: selection.run, tag: selection.tag, }; - const matchResult: MatchResultType = await requestAddMatchNodes(npuNodeName, benchNodeName, metaData); + const matchResult: MatchResultType = await requestAddMatchNodes(npuNodeName, benchNodeName, metaData, isMatchChildren); return matchResult; }; - const deleteMatchedNodesLink = async (npuNodeName: string, benchNodeName: string, selection: any): Promise => { + const deleteMatchedNodesLink = async (npuNodeName: string, benchNodeName: string, selection: any, isUnMatchChildren: boolean): Promise => { if (isEmpty(npuNodeName) || isEmpty(benchNodeName)) { return { success: false, @@ -115,7 +118,7 @@ const useMatched = (): UseMatchedType => { run: selection.run, tag: selection.tag, }; - const matchResult: MatchResultType = await requestDeleteMatchNodes(npuNodeName, benchNodeName, metaData); + const matchResult: MatchResultType = await requestDeleteMatchNodes(npuNodeName, benchNodeName, metaData, isUnMatchChildren); return matchResult; }; diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/type/index.d.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/type/index.d.ts index 94b2d6698c0..e4339fb2acb 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/type/index.d.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_controls_board/type/index.d.ts @@ -23,8 +23,8 @@ export type MetaDirType = Record>; export interface UseMatchedType { saveMatchedNodesLink: (selection: any) => Promise; - addMatchedNodesLink: (npuNodeName: string, benchNodeName: string, selection: any) => Promise; - deleteMatchedNodesLink: (npuNodeName: string, benchNodeName: string, selection: any) => Promise; + addMatchedNodesLink: (npuNodeName: string, benchNodeName: string, selection: any, isMatchChildren: boolean) => Promise; + deleteMatchedNodesLink: (npuNodeName: string, benchNodeName: string, selection: any, isUnMatchChildren: boolean) => Promise; saveMatchedRelations: (selection: any) => Promise; addMatchedNodesLinkByConfigFile: (condfigFile: string, selection: any) => Promise; } diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py index 70ef0b2172a..202707c85a8 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/service/graph_service.py @@ -262,17 +262,30 @@ class GraphService: return {'success': False, 'error': '获取节点信息失败:' + str(e), 'data': None} @staticmethod - def add_match_nodes(npu_node_name, bench_node_name, meta_data): + def add_match_nodes(npu_node_name, bench_node_name, meta_data, is_match_children): graph_data, error_message = GraphUtils.get_graph_data(meta_data) if error_message: return {'success': False, 'error': error_message} task = graph_data.get('task') + result = {} try: # 根据任务类型计算误差 if task == 'md5' or task == 'summary': - result = MatchNodesController.process_task_add_child_layer(graph_data, - npu_node_name, bench_node_name, task) - return result + if(is_match_children): + result = MatchNodesController.process_task_add_child_layer(graph_data, + npu_node_name, bench_node_name, task) + return result + else: + result = MatchNodesController.process_task_add(graph_data, npu_node_name, bench_node_name, task) + if result.get('success'): + config_data = GraphState.get_global_value("config_data") + result['data'] = { + 'npuMatchNodes': config_data.get('npuMatchNodes', {}), + 'benchMatchNodes': config_data.get('benchMatchNodes', {}), + 'npuUnMatchNodes': config_data.get('npuUnMatchNodes', []), + 'benchUnMatchNodes': config_data.get('benchUnMatchNodes', []) + } + return result else: return {'success': False, 'error': '任务类型不支持(Task type not supported) '} except Exception as e: @@ -298,16 +311,28 @@ class GraphService: return {'success': False, 'error': '操作失败', 'data': None} @staticmethod - def delete_match_nodes(npu_node_name, bench_node_name, meta_data): + def delete_match_nodes(npu_node_name, bench_node_name, meta_data, is_unmatch_children): graph_data, error_message = GraphUtils.get_graph_data(meta_data) if error_message: return {'success': False, 'error': error_message} task = graph_data.get('task') + result = {} try: # 根据任务类型计算误差 if task == 'md5' or task == 'summary': - result = MatchNodesController.process_task_delete_child_layer(graph_data, npu_node_name, + if(is_unmatch_children): + result = MatchNodesController.process_task_delete_child_layer(graph_data, npu_node_name, bench_node_name, task) + else: + result = MatchNodesController.process_task_delete(graph_data, npu_node_name, bench_node_name, task) + if result.get('success'): + config_data = GraphState.get_global_value("config_data") + result['data'] = { + 'npuMatchNodes': config_data.get('npuMatchNodes', {}), + 'benchMatchNodes': config_data.get('benchMatchNodes', {}), + 'npuUnMatchNodes': config_data.get('npuUnMatchNodes', []), + 'benchUnMatchNodes': config_data.get('benchUnMatchNodes', []) + } return result else: return {'success': False, 'error': '任务类型不支持(Task type not supported) '} diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/views/graph_views.py b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/views/graph_views.py index 1ef7130e8b9..85762222b90 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/server/app/views/graph_views.py +++ b/plugins/tensorboard-plugins/tb_graph_ascend/server/app/views/graph_views.py @@ -135,7 +135,8 @@ class GraphView: npu_node_name = request.args.get("npuNodeName") bench_node_name = request.args.get("benchNodeName") meta_data = GraphUtils.safe_json_loads(request.args.get("metaData")) - match_result = GraphService.add_match_nodes(npu_node_name, bench_node_name, meta_data) + is_match_children = GraphUtils.safe_json_loads(request.args.get("isMatchChildren")) + match_result = GraphService.add_match_nodes(npu_node_name, bench_node_name, meta_data, is_match_children) return http_util.Respond(request, json.dumps(match_result), "application/json") # 取消节点匹配 @@ -145,7 +146,8 @@ class GraphView: npu_node_name = request.args.get("npuNodeName") bench_node_name = request.args.get("benchNodeName") meta_data = GraphUtils.safe_json_loads(request.args.get("metaData")) - match_result = GraphService.delete_match_nodes(npu_node_name, bench_node_name, meta_data) + is_unmatch_children = GraphUtils.safe_json_loads(request.args.get("isUnMatchChildren")) + match_result = GraphService.delete_match_nodes(npu_node_name, bench_node_name, meta_data, is_unmatch_children) return http_util.Respond(request, json.dumps(match_result), "application/json") # 保存匹配节点列表 -- Gitee From c980a7d64b8f946faf8a64111f6c5d428c9ab917 Mon Sep 17 00:00:00 2001 From: sunchao <1299792067@qq.com> Date: Wed, 2 Jul 2025 11:41:28 +0800 Subject: [PATCH 10/18] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=E5=85=BC=E9=A1=BE?= =?UTF-8?q?=E6=98=93=E7=94=A8=E6=80=A7=E9=97=AE=E9=A2=98=E5=92=8C=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tb_graph_ascend/fe/package-lock.json | 239 ++++++++++++++++++ .../tb_graph_ascend/fe/package.json | 1 + .../fe/src/graph_ascend/index.ts | 51 +++- .../fe/src/graph_ascend/type/index.d.ts | 13 + .../fe/src/graph_ascend/useGraphAscend.ts | 21 +- .../tb_graph_ascend/fe/src/index.css | 5 + .../server/app/service/graph_service.py | 16 +- .../server/app/views/graph_views.py | 3 +- 8 files changed, 336 insertions(+), 13 deletions(-) diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json index 0e2e1896ed6..d58f92bf241 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package-lock.json @@ -31,6 +31,7 @@ "@vaadin/button": "24.6.5", "@vaadin/checkbox": "^24.8.2", "@vaadin/combo-box": "24.6.5", + "@vaadin/confirm-dialog": "^24.8.2", "@vaadin/context-menu": "24.6.5", "@vaadin/details": "24.6.5", "@vaadin/grid": "24.6.5", @@ -1382,6 +1383,129 @@ "lit": "^3.0.0" } }, + "node_modules/@vaadin/confirm-dialog": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/confirm-dialog/-/confirm-dialog-24.8.2.tgz", + "integrity": "sha512-JIAhOYvhSlJbPDZ+EKbKENa2xsYy8fTlUO8cWJxJvDs1p/MbPCGvc9rhmHlKCHJJEGXyN23sS08ef7NHX7d9CA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/button": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "@vaadin/dialog": "~24.8.2", + "@vaadin/overlay": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/a11y-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/a11y-base/-/a11y-base-24.8.2.tgz", + "integrity": "sha512-Cp2raqWoe/sgzuvpSo17ROxEvTMj+3gYfW8uAkCEC2bVmV542oMiPzLPyC0GP2GjJGzrvz7y9eQ3sq1ATZxLkA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/button": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/button/-/button-24.8.2.tgz", + "integrity": "sha512-wuJn+aa4QfWC1KW2D4S6B8Qbh39MZ+/GoBVqjldU5KSksJEZFFePym0SmpRodvuSuoxrFKote7CxtthOY4TwXw==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/a11y-base": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/component-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/component-base/-/component-base-24.8.2.tgz", + "integrity": "sha512-pzM49fDKm4erK5GDIvakb0utCm8Xm7oBqkxyGJfgS3y9w8MGvJ6foUL/QD+13nW5zL6toZ+kwdIw7PvIF96pKQ==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/vaadin-development-mode-detector": "^2.0.0", + "@vaadin/vaadin-usage-statistics": "^2.1.0", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/icon": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.8.2.tgz", + "integrity": "sha512-0IhbiikTDJzpn9NQplKIIW2lK5tK6q2pLHXT+O41KA2ZcwTWoyAoKQyjU40vpHK+9EOGhwqX7LncugnWSO6iwg==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/overlay": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/overlay/-/overlay-24.8.2.tgz", + "integrity": "sha512-DsmynLnU2KCNNRQe/0qHfRHQoSe/oZtrbzTGp7xyjwmU2sPLH4fXNShUbXi7MegLJs/YOH1QoIh8ejXNzxNuCA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/a11y-base": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/vaadin-lumo-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-24.8.2.tgz", + "integrity": "sha512-DanIV4ZHjmb/1dF+oyj33IZVxEXLa1Sjb3ZDWrTlu6I59/veDsFE7g6y6ZYjNLE/O8YlE1oz8yHEUViSXmBwfw==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/icon": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/vaadin-material-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-24.8.2.tgz", + "integrity": "sha512-l0WMqPeH0bfoviZqu96h9CFZ92Y7nQwG9gb1mr9tkXd7XCbt/DEmCcvVaAJSpxVp+Qi7/J8z8Wn9855GExTdHg==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/confirm-dialog/node_modules/@vaadin/vaadin-themable-mixin": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-24.8.2.tgz", + "integrity": "sha512-i0MLfbGU+B1fWEPxAQzrQOdQ2JfrYLyagOfe8FOKFAMm03xURtbD7hZKxFchULudphD+/NvHh2i++Vl3mGMfsA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "lit": "^3.0.0", + "style-observer": "^0.0.8" + } + }, "node_modules/@vaadin/context-menu": { "version": "24.6.5", "resolved": "https://registry.npmmirror.com/@vaadin/context-menu/-/context-menu-24.6.5.tgz", @@ -1419,6 +1543,121 @@ "lit": "^3.0.0" } }, + "node_modules/@vaadin/dialog": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/dialog/-/dialog-24.8.2.tgz", + "integrity": "sha512-DLUt4nPKQtTVBqQFwq4nz+TRht0uHFfNh/u/uR/7WdUISR7EwJ/L8XztiJO2tbv16toYl3FrX7Us8ybsHZIqKA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/lit-renderer": "~24.8.2", + "@vaadin/overlay": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/a11y-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/a11y-base/-/a11y-base-24.8.2.tgz", + "integrity": "sha512-Cp2raqWoe/sgzuvpSo17ROxEvTMj+3gYfW8uAkCEC2bVmV542oMiPzLPyC0GP2GjJGzrvz7y9eQ3sq1ATZxLkA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/component-base": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/component-base/-/component-base-24.8.2.tgz", + "integrity": "sha512-pzM49fDKm4erK5GDIvakb0utCm8Xm7oBqkxyGJfgS3y9w8MGvJ6foUL/QD+13nW5zL6toZ+kwdIw7PvIF96pKQ==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/vaadin-development-mode-detector": "^2.0.0", + "@vaadin/vaadin-usage-statistics": "^2.1.0", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/icon": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/icon/-/icon-24.8.2.tgz", + "integrity": "sha512-0IhbiikTDJzpn9NQplKIIW2lK5tK6q2pLHXT+O41KA2ZcwTWoyAoKQyjU40vpHK+9EOGhwqX7LncugnWSO6iwg==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/lit-renderer": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/lit-renderer/-/lit-renderer-24.8.2.tgz", + "integrity": "sha512-5xNRhD6ZPWLpvbCZ4mg/V6W93v3ohUyP/ULKk/6Hzxe05w6iYsel2xNEzoycmdQKIb/k24xZQMz53KDT8fyroQ==", + "license": "Apache-2.0", + "dependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/overlay": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/overlay/-/overlay-24.8.2.tgz", + "integrity": "sha512-DsmynLnU2KCNNRQe/0qHfRHQoSe/oZtrbzTGp7xyjwmU2sPLH4fXNShUbXi7MegLJs/YOH1QoIh8ejXNzxNuCA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "@polymer/polymer": "^3.0.0", + "@vaadin/a11y-base": "~24.8.2", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-lumo-styles": "~24.8.2", + "@vaadin/vaadin-material-styles": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2", + "lit": "^3.0.0" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/vaadin-lumo-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-lumo-styles/-/vaadin-lumo-styles-24.8.2.tgz", + "integrity": "sha512-DanIV4ZHjmb/1dF+oyj33IZVxEXLa1Sjb3ZDWrTlu6I59/veDsFE7g6y6ZYjNLE/O8YlE1oz8yHEUViSXmBwfw==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/icon": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/vaadin-material-styles": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-material-styles/-/vaadin-material-styles-24.8.2.tgz", + "integrity": "sha512-l0WMqPeH0bfoviZqu96h9CFZ92Y7nQwG9gb1mr9tkXd7XCbt/DEmCcvVaAJSpxVp+Qi7/J8z8Wn9855GExTdHg==", + "license": "Apache-2.0", + "dependencies": { + "@polymer/polymer": "^3.0.0", + "@vaadin/component-base": "~24.8.2", + "@vaadin/vaadin-themable-mixin": "~24.8.2" + } + }, + "node_modules/@vaadin/dialog/node_modules/@vaadin/vaadin-themable-mixin": { + "version": "24.8.2", + "resolved": "https://registry.npmmirror.com/@vaadin/vaadin-themable-mixin/-/vaadin-themable-mixin-24.8.2.tgz", + "integrity": "sha512-i0MLfbGU+B1fWEPxAQzrQOdQ2JfrYLyagOfe8FOKFAMm03xURtbD7hZKxFchULudphD+/NvHh2i++Vl3mGMfsA==", + "license": "Apache-2.0", + "dependencies": { + "@open-wc/dedupe-mixin": "^1.3.0", + "lit": "^3.0.0", + "style-observer": "^0.0.8" + } + }, "node_modules/@vaadin/field-base": { "version": "24.6.11", "resolved": "https://registry.npmmirror.com/@vaadin/field-base/-/field-base-24.6.11.tgz", diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json index 6bb4470b360..3aacd5be117 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/package.json @@ -52,6 +52,7 @@ "@vaadin/button": "24.6.5", "@vaadin/checkbox": "^24.8.2", "@vaadin/combo-box": "24.6.5", + "@vaadin/confirm-dialog": "^24.8.2", "@vaadin/context-menu": "24.6.5", "@vaadin/details": "24.6.5", "@vaadin/grid": "24.6.5", diff --git a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts index 8a3cff935c0..171bb15d272 100644 --- a/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts +++ b/plugins/tensorboard-plugins/tb_graph_ascend/fe/src/graph_ascend/index.ts @@ -17,12 +17,17 @@ import { customElement, observe, property } from '@polymer/decorators'; import { html, PolymerElement } from '@polymer/polymer'; import { LegacyElementMixin } from '../polymer/legacy_element_mixin'; +import { loadGraphFileInfoListType } from './type'; import useGraphAscend from './useGraphAscend'; import { formatBytes, safeJSONParse } from '../utils'; +import { isEmpty } from 'lodash'; import '../graph_board/index'; import '../graph_info_board/index'; import '../graph_controls_board/index'; import '../common/graph-board-layout'; +import '@vaadin/confirm-dialog' +import { Notification } from '@vaadin/notification'; + import type { SelectionType, ProgressType, GraphConfigType, GraphAllNodeType, NodeListType, UnmatchedNodeType } from './type'; @customElement('graph-ascend') @@ -82,7 +87,21 @@ class TfGraphDashboard extends LegacyElementMixin(PolymerElement) { - + +
+ +
+