Repository: SwanHubX/SwanLab (language: python)

Issue:
change dependence
## Description

对于数据处理部分的可选依赖，不应该直接用类似：

```python
from swanlab.data.modules.utils import check_library_installed

check_library_installed("soundfile")
check_library_installed("numpy")
```

的形式，因为这些类被swanlab导出，所以无论怎么样，这些代码都会执行。比较好的方式是类似：

```python
try:
    # noinspection PyPackageRequirements
    import soundfile as sf
    # noinspection PyPackageRequirements
    import numpy as np
except ImportError:
    sf, np = None, None
```

然后在使用类__init__方法中判断是否可以使用，类似：

```python

  def __init__(
          self,
          data_or_path: Union[str, np.ndarray],
          sample_rate: int = 44100,
          caption: str = None,
  ):
      """Audio class constructor

      Parameters
      ----------
      data_or_path: str or numpy.ndarray
          Path to an audio file or numpy array of audio data.
      sample_rate: int
              Sample rate of the audio data. Required when input is numpy array.
      caption: str
          Caption for the audio.
      """
      if sf is None or np is None:
          raise ImportError(
              "soundfile and numpy are required for Audio class, "
              'you can install them by `pip install "swanlab[process]"`'
          )
      ...
```


然后就可以使用`pip install "swanlab[process]"`的方式安装了

----


但是其实并不是，之所以可以使用`pip install "swanlab[process]"`的方式安装是因为我进行了一些配置。

另外，本pr删除了对ujson的依赖。
update test doc
## Description

Please include a concise summary, in clear English, of the changes in this pull request. If it closes an issue, please
mention it here.

Closes: #(issue)

## 🎯 PRs Should Target Issues

Before your create a PR, please check to see if there is [an existing issue](https://github.com/SwanHubX/SwanLab/issues)
for this change. If not, please create an issue before you create this PR, unless the fix is very small.

Not adhering to this guideline will result in the PR being closed.

<!-- ## Tests -->
<!-- There are no hive tests yet -->



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 4af844da..725242eb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,7 +11,8 @@ package-lock.json
 # test
 temp/
 tutils/config.json
-tutils/package.mock.json
+package.mock.json
+.env
 
 # playground
 playground/
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index ecc2a364..8761f940 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -5,20 +5,23 @@
 ## 📦 目录
 
 - [标准开发流程](#标准开发流程)
-- [调试流程](#本地调试)
-  - [IDE与插件](#IDE与插件)
-  - [配置npm与Python环境](#配置npm与Python环境)
-  - [调试脚本](#开发调试)
-  - [调试流程](#调试流程)
-- [FAQ](#FAQ)
+- [本地调试](#本地调试)
+  - [IDE 与插件](#IDE与插件)
+  - [配置 Python 环境](#配置python环境)
+  - [调试脚本](#调试脚本)
+- [本地测试](#本地测试)
+  - [python 脚本调试](#python-脚本调试)
+  - [单元测试](#单元测试)
 
 ## 标准开发流程
 
-1. 浏览 GitHub 上的[Issues](https://github.com/SwanHubX/SwanLab/issues)，查看你愿意添加的功能或修复的错误，以及它们是否已被 Pull Request。
+1. 浏览 GitHub 上的[Issues](https://github.com/SwanHubX/SwanLab/issues)，查看你愿意添加的功能或修复的错误，以及它们是否已被
+   Pull Request。
 
    - 如果没有，请创建一个[新 Issue](https://github.com/SwanHubX/SwanLab/issues/new/choose)——这将帮助项目跟踪功能请求和错误报告，并确保不重复工作。
 
-2. 如果你是第一次为开源项目贡献代码，请转到`https://github.com/SwanHubX/SwanLab`并单击右上角的"Fork"按钮。这将创建你用于开发的仓库的个人副本。
+2. 如果你是第一次为开源项目贡献代码，请转到 [本项目首页](https://github.com/SwanHubX/SwanLab) 并单击右上角的"Fork"
+   按钮。这将创建你用于开发的仓库的个人副本。
 
    - 将 Fork 的项目克隆到你的计算机，并添加指向`swanlab`项目的远程链接：
 
@@ -50,8 +53,9 @@
    git commit -m "feat(integrations): Add integration with the `awesomepyml` library"
    ```
 
-   4.通过[Github Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)发起贡献：
+4. 发起贡献：
 
+   - [Github Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests)
    - 当您的贡献准备就绪后，将您的分支推送到 GitHub：
 
    ```bash
@@ -60,11 +64,12 @@
 
    - 分支上传后， `GitHub`将打印一个 URL，用于将您的贡献作为拉取请求提交。在浏览器中打开该 URL，为您的拉取请求编写信息丰富的标题和详细描述，然后提交。
 
-   - 请将相关 Issue（现有 Issue 或您创建的 Issue）链接到您的 PR。请参阅 PR 页面的右栏。或者，在 PR 描述中提及“修复问题链接” - GitHub 将自动进行链接。
+   - 请将相关 Issue（现有 Issue 或您创建的 Issue）链接到您的 PR。请参阅 PR 页面的右栏。或者，在 PR
+     描述中提及“修复问题链接” - GitHub 将自动进行链接。
 
    - 我们将审查您的贡献并提供反馈。要合并审阅者建议的更改，请将编辑提交到您的分支，然后再次推送到分支（无需重新创建拉取请求，它将自动跟踪对分支的修改），例如：
 
-   ```python
+   ```bash
    git add tests/test-changed-file.py
    git commit -m "test(sdk): Add a test case to address reviewer feedback"
    git push origin <username>/<short-dash-seperated-feature-description>
@@ -74,21 +79,22 @@
 
 ## 本地调试
 
-### IDE与插件
+### IDE 与插件
 
-1. **使用VSCode作为你的开发IDE**
+1. **使用 VSCode 作为你的开发 IDE**
 
-SwanLab仓库已经配好了[VSCode](https://code.visualstudio.com/)的环境、插件与调试脚本（位于`.vscode`文件夹中），使用VSCode开发SwanLab会有最好的体验。
+   SwanLab 仓库已经配好了[VSCode](https://code.visualstudio.com/)的环境、插件与调试脚本（位于`.vscode`
+   文件夹中），使用 VSCode 开发 SwanLab 会有最好的体验。
 
-2. **安装VSCode插件（可选）**
+2. **安装 VSCode 插件（可选）**
 
-用VSCode打开项目，进入 [扩展] ，在搜索框输入“@recommended”，会出现一系列推荐插件，推荐全部安装这些插件。
+   用 VSCode 打开项目，进入 [扩展] ，在搜索框输入“@recommended”，会出现一系列推荐插件，推荐全部安装这些插件。
 
-![vscode-recommend](/readme_files/contribution_images/vscode_recommend.png)
+   ![vscode-recommend](/readme_files/contribution_images/vscode_recommend.png)
 
-### 配置Python环境
+### 配置 Python 环境
 
-SwanLab项目环境需要`python>=3.8`的支持。
+SwanLab 项目环境需要`python>=3.8`的支持。
 
 必须性的 python 依赖集中记录在项目根目录下的 `requirements.txt`。
 
@@ -97,6 +103,7 @@ SwanLab项目环境需要`python>=3.8`的支持。
 ```Bash
 # swanlab所依赖的包
 pip install -r requirements.txt
+pip install -r requirements-media.txt
 ```
 
 编译、开发、单元测试等工作需要使用以下命令额外安装依赖：
@@ -106,9 +113,9 @@ pip install -r requirements.txt
 pip install -r requirements-dev.txt
 ```
 
-## 调试脚本
+### 调试脚本
 
-1. **VSCode调试脚本**
+1. **VSCode 调试脚本**
 
 在 VSCode-运行和调试 中，项目配置好了一系列调试脚本：
 
@@ -116,24 +123,41 @@ pip install -r requirements-dev.txt
 
 - **开启一个实验**：运行`test/create_experiment.py`脚本
 
-- **运行当前文件**：使用配置好的Python环境运行你选中的文件
+- **运行当前文件**：使用配置好的 Python 环境运行你选中的文件
+
+- **测试当前文件**：使用 debug 模式测试你选中的文件
+
+- **进行所有单元测试**：运行`test/unit`中的脚本对 swanlab 基础功能进行完整单元测试
+
+- **构建项目**：打包项目为 whl 文件（pip 安装包格式）
+
+Ps: 如果你不想使用 VSCode 进行开发，可以前往`.vscode/launch.json`，查看每个调试项对应的命令，了解其配置。
 
-- **测试当前文件**：使用debug模式测试你选中的文件
+## 本地测试
 
-- **进行所有单元测试**：运行`test/unit`中的脚本对swanlab基础功能进行完整单元测试
+进行测试的前提是你已经安装完毕所有的所需依赖。
 
-- **构建项目**：打包项目为whl文件（pip安装包格式）
+### python 脚本调试
 
-Ps: 如果你不想使用VSCode进行开发，可以前往`.vscode/launch.json`，查看每个调试项对应的命令。
+在完成你的改动后，可以将你用于测试的 python 脚本放到根目录或`test`文件夹下，然后通过[VSCode 脚本](#调试脚本)中的"运行当前文件"来运行你的 Python 测试脚本, 这样你的脚本运行将使用到已改动后的 swanlab。
 
-## 调试流程
+### 单元测试
 
-<=================== 已过期，记得更改 ===================>
+可以通过[VSCode 脚本](#调试脚本)或者在项目根目录下运行以下命令进行单元测试：
 
-- 首次调试时，依次启动脚本：**开启一个实验 -> 后端开发 -> 前端开发**
+```Bash
+export PYTHONPATH=. && pytest test/unit
+```
+
+由于 swanlab 涉及与云端的交互，而云端部分是闭源的，所以如果你是第一次贡献代码，可能无法进行完整的测试。
+针对这种情况，请在本地根目录下创建`.env`文件，并填写如下环境变量配置：
+
+```dotenv
+TEST_CLOUD_SKIP=true
+```
 
-  - 第1次执行“开启一个实验”会在根目录下创建日志文件夹（默认为`swanlog`），“后端开发”将基于这个文件夹开启。
+这样就可以跳过云端测试，只进行本地的部分功能测试。
 
-- 后续调试时，依次启动脚本：**后端开发 -> 前端开发 -> 开启一个实验**
+> 事实上`TEST_CLOUD_SKIP`环境变量可以是任意值，只要存在即可跳过云端测试。
 
-  - 因为已经存在日志文件夹，所以“后端开发”直接可以启用。
+如果想进行完整的测试，请联系项目维护者，我们会提供测试环境的配置。
diff --git a/pyproject.toml b/pyproject.toml
index 42e3261a..6fc058c8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -10,7 +10,7 @@ build-backend = "hatchling.build"
 
 [project]
 name = "swanlab" # 项目名称
-dynamic = ["version", "dependencies", "readme"] # 动态配置版本，依赖，可选依赖，readme
+dynamic = ["version", "dependencies", "optional-dependencies", "readme"] # 动态配置版本，依赖，可选依赖，readme
 description = "Python library for streamlined tracking and management of AI training processes." # 项目描述
 license = "Apache-2.0" # 项目许可证
 requires-python = ">=3.8" # python版本要求，我们只维护python3.8以上版本
@@ -56,6 +56,9 @@ pattern = ".*\"version\":\\s*\"(?P<version>[^\"]+)\""
 [tool.hatch.metadata.hooks.requirements_txt]
 filename = "requirements.txt"
 
+[tool.hatch.metadata.hooks.requirements_txt.optional-dependencies]
+media = ["requirements-media.txt"]  # pip install "swanlab[meida]"
+
 [tool.hatch.metadata.hooks.fancy-pypi-readme]  # 动态设置readme
 content-type = "text/markdown"
 fragments = [{ path = "README.md" }]
@@ -87,7 +90,7 @@ include = [
     "/test", # 包含一些测试脚本，确保测试成功
     "/README.md", # 包含readme
     "/requirements.txt", # 包含依赖
-    "/requirements-swan.txt", # 包含可选依赖
+    "/requirements-media.txt", # 包含可选依赖
 ]
 
 [tool.hatch.build.targets.wheel]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 3d6d778b..8b42f18d 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,17 +1,10 @@
-# pytest
-
 build
-
 pytest
-
-pytest-asyncio
-
 nanoid
 omegaconf
 ipython
 ipython<8.13; python_version < '3.9'
 ipykernel
-
-# Packages used in unit test scripts
 torch
-torchvision
\ No newline at end of file
+torchvision
+python-dotenv
\ No newline at end of file
diff --git a/requirements-media.txt b/requirements-media.txt
new file mode 100644
index 00000000..008a5bd2
--- /dev/null
+++ b/requirements-media.txt
@@ -0,0 +1,4 @@
+soundfile
+pillow
+matplotlib
+numpy
\ No newline at end of file
diff --git a/requirements.txt b/requirements.txt
index 294b9c54..783fb078 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,26 +1,8 @@
-# for swanlab
-swankit==0.1.0b7
-swanboard==0.1.2b3
-
-# data process
-soundfile
-pillow
-matplotlib
-numpy
-
-# web server
+swankit==0.1.0b10
+swanboard==0.1.2b8
 cos-python-sdk-v5
-fastapi>=0.110.1
-uvicorn>=0.14.0
 requests
 click
-
-
-# database
-ujson
 pyyaml
-
-
-# Information collection
 psutil
 pynvml
diff --git a/swanlab/api/auth/login.py b/swanlab/api/auth/login.py
index 9b4125ad..5c85bd7f 100644
--- a/swanlab/api/auth/login.py
+++ b/swanlab/api/auth/login.py
@@ -9,11 +9,11 @@
     进行一些交互定义和数据请求
 """
 from swanlab.error import ValidationError
-from swanlab.utils import FONT
+from swankit.log import FONT
 from swanlab.package import get_user_setting_path, get_host_api
 from swanlab.api.info import LoginInfo
 from swanlab.log import swanlog
-from swanlab.utils.judgment import in_jupyter
+from swanlab.env import in_jupyter
 import getpass
 import requests
 import sys
@@ -46,8 +46,8 @@ def login_by_key(api_key: str, timeout: int = 20, save: bool = True) -> LoginInf
 
 
 def input_api_key(
-    tip: str = "Paste an API key from your profile and hit enter, or press 'CTRL-C' to quit: ",
-    again: bool = False,
+        tip: str = "Paste an API key from your profile and hit enter, or press 'CTRL-C' to quit: ",
+        again: bool = False,
 ) -> str:
     """让用户输入apikey
     此时有两条消息，第一条消息为固定格式，第二条消息
@@ -112,7 +112,7 @@ def terminal_login(api_key: str = None) -> LoginInfo:
                 raise e
 
 
-def _abort_tip(tp, val, tb):
+def _abort_tip(tp, _, __):
     """处理用户在input_api_key输入时按下CTRL+C的情况"""
     if tp == KeyboardInterrupt:
         print("\n" + FONT.red("Aborted!"))
diff --git a/swanlab/api/http.py b/swanlab/api/http.py
index 3a95469d..428230f4 100644
--- a/swanlab/api/http.py
+++ b/swanlab/api/http.py
@@ -15,7 +15,7 @@
 from swanlab.data.modules import MediaBuffer
 from swanlab.error import NetworkError, ApiError
 from swanlab.package import get_host_api
-from swanlab.utils import FONT
+from swankit.log import FONT
 from swanlab.log import swanlog
 import requests
 
diff --git a/swanlab/api/info.py b/swanlab/api/info.py
index 69d82dcf..b90bb5f5 100644
--- a/swanlab/api/info.py
+++ b/swanlab/api/info.py
@@ -7,11 +7,8 @@
 @Description:
     定义认证数据格式
 """
-import os.path
 import requests
-from swanlab.utils.key import save_key
-from swanlab.env import get_swanlab_folder
-from swanlab.package import get_host_api
+from swanlab.package import save_key
 from typing import Union
 
 
@@ -90,8 +87,7 @@ def save(self):
         """
         保存登录信息
         """
-        path = os.path.join(get_swanlab_folder(), ".netrc")
-        return save_key(path, get_host_api(), "user", self.api_key)
+        return save_key("user", self.api_key)
 
 
 class ProjectInfo:
diff --git a/swanlab/cli/commands/auth/login.py b/swanlab/cli/commands/auth/login.py
index 8d625f7c..49f182d4 100644
--- a/swanlab/cli/commands/auth/login.py
+++ b/swanlab/cli/commands/auth/login.py
@@ -10,7 +10,7 @@
 import click
 from swanlab.package import is_login
 from swanlab.api.auth import terminal_login
-from swanlab.utils import FONT
+from swankit.log import FONT
 
 
 @click.command()
diff --git a/swanlab/cli/commands/auth/logout.py b/swanlab/cli/commands/auth/logout.py
index 45e73906..1f0d5cbe 100644
--- a/swanlab/cli/commands/auth/logout.py
+++ b/swanlab/cli/commands/auth/logout.py
@@ -9,8 +9,8 @@
 """
 import click
 from swanlab.package import is_login
-from swanlab.utils import FONT
-from swanlab.env import get_swanlab_folder
+from swankit.log import FONT
+from swanlab.env import get_save_dir
 import shutil
 import sys
 
@@ -24,7 +24,7 @@ def logout():
         confirm = input(FONT.swanlab("Are you sure you want to logout? (y/N): "))
         if confirm.lower() == "y":
             try:
-                shutil.rmtree(get_swanlab_folder())
+                shutil.rmtree(get_save_dir())
                 return print(FONT.swanlab("Logout successfully. You can use `" + command + "` to login again."))
             except Exception as e:
                 return print(FONT.swanlab("Logout failed. Reason:" + str(e)))
diff --git a/swanlab/cli/commands/dashboard/watch.py b/swanlab/cli/commands/dashboard/watch.py
index fd197840..c80ccd43 100644
--- a/swanlab/cli/commands/dashboard/watch.py
+++ b/swanlab/cli/commands/dashboard/watch.py
@@ -7,7 +7,7 @@
 @Description:
     watch命令
 """
-from swanlab.env import get_swanlog_dir, ROOT, PORT, HOST
+from swanlab.env import get_swanlog_dir, SwanLabEnv
 from swanlab.log import swanlog
 from swanboard import SwanBoardRun
 import click
@@ -18,7 +18,7 @@
 @click.command()
 @click.argument(
     "path",
-    envvar=ROOT,
+    envvar=SwanLabEnv.SWANLOG_FOLDER.value,
     type=click.Path(
         exists=True,
         dir_okay=True,
@@ -32,7 +32,7 @@
 @click.option(
     "--host",
     "-h",
-    default=lambda: os.environ.get(HOST, "127.0.0.1"),
+    default=lambda: os.environ.get(SwanLabEnv.SWANBOARD_HOST.value, "127.0.0.1"),
     type=str,
     nargs=1,
     help="The host of swanlab web, default by 127.0.0.1",
@@ -40,7 +40,7 @@
 @click.option(
     "--port",
     "-p",
-    default=lambda: os.environ.get(PORT, 5092),
+    default=lambda: os.environ.get(SwanLabEnv.SWANBOARD_PROT.value, 5092),
     nargs=1,
     type=click.IntRange(1, 65535),
     help="The port of swanlab web, default by 5092",
@@ -48,7 +48,7 @@
 @click.option(
     "--logdir",
     "-l",
-    default=lambda: os.environ.get(ROOT, None),
+    default=lambda: os.environ.get(SwanLabEnv.SWANLOG_FOLDER.value, None),
     nargs=1,
     type=click.Path(
         exists=True,
@@ -82,10 +82,13 @@ def watch(path: str, host: str, port: int, logdir: str, log_level: str):
     path = logdir if logdir is not None else path
     if path is not None:
         path = os.path.abspath(path)
-        os.environ[ROOT] = path
+        os.environ[SwanLabEnv.SWANLOG_FOLDER.value] = path
     # 为None时从环境变量中获取
     try:
         path = get_swanlog_dir()
+        # 产品经理要求无论日志文件夹是否存在都不报错 🤡 ，给用户以开启web服务的“爽感”
+        # if not os.path.exists(path):
+        #     raise FileNotFoundError
     except ValueError as e:
         click.BadParameter(str(e))
         return sys.exit(3)
diff --git a/swanlab/data/callback_cloud.py b/swanlab/data/callback_cloud.py
index 83116bb2..068fa132 100644
--- a/swanlab/data/callback_cloud.py
+++ b/swanlab/data/callback_cloud.py
@@ -7,29 +7,109 @@
 @Description:
     云端回调
 """
-from .run.callback import MetricInfo, ColumnInfo, RuntimeInfo
+from swankit.callback import RuntimeInfo, MetricInfo, ColumnInfo
+from swankit.core import SwanLabSharedSettings
+
 from swanlab.data.cloud import UploadType
 from swanlab.api.upload.model import ColumnModel, ScalarModel, MediaModel, FileModel
 from swanlab.api import LoginInfo, create_http, terminal_login
 from swanlab.api.upload import upload_logs
 from swanlab.log import swanlog
-from swanlab.utils.font import FONT
 from swanlab.api import get_http
-from swanlab.utils.key import get_key
-from swanlab.utils.judgment import in_jupyter, show_button_html
-from swanlab.package import get_host_web, get_host_api
+from swanlab.env import in_jupyter
+from swanlab.package import get_host_web, get_key
 from swanlab.error import KeyFileError
-from swanlab.env import get_swanlab_folder
 from .callback_local import LocalRunCallback, get_run, SwanLabRunState
 from swanlab.data.cloud import ThreadPool
-from swanlab.utils import create_time
 from swanlab.package import get_package_version, get_package_latest_version
+from swankit.log import FONT
+from swankit.env import create_time
 import json
 import sys
 import os
 import io
 
 
+def show_button_html(experiment_url):
+    """
+    用于在jupyter前端显示云端的环境iframe和按钮
+    :param experiment_url: 实验链接
+    """
+    try:
+        # noinspection PyPackageRequirements
+        from IPython.display import HTML, display
+
+        iframe_h5 = f'<iframe src="{experiment_url}" width=100% height="600" frameborder="no"></iframe>'
+        js_code = f"""
+        <script>
+            function showIframe() {{
+                var iframeHtml = '{iframe_h5}';
+                document.getElementById('iframeContainer').innerHTML = iframeHtml;
+            }}
+        </script>
+        """
+
+        total_h5 = f"""
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>Show Iframe</title>
+    {js_code}
+</head>
+<body>
+    <style>
+        .interactive-button {{
+            display: flex;
+            align-items: center;
+            height: 36px;
+            border: 0px;
+            background-color: #2c8f63;
+            color: white;
+            padding: 10px 20px;
+            transition: background-color 0.3s, transform 0.2s;
+        }}
+
+        .interactive-button:hover {{
+            background-color: #5cab87;
+            cursor: pointer;
+        }}
+
+        .interactive-button:active {{
+            background-color: #217952;
+            transform: scale(0.96);
+        }}
+    </style>
+    <br>
+    <button onclick="showIframe()" class="interactive-button">
+        <svg style="height: 16px; margin-right: 8px;" viewBox="0 0 46 46" fill="none">
+            <path
+                d="M10.8439 21.1974C10.6414 21.2854 10.4477 21.3925 10.2655 21.5173L10.2069 21.5652C10.1839 21.58 10.1625 21.5969 10.1429 21.6159C6.29135 24.6118 4.22831 29.4416 5.32646 34.282C5.94656 37.0577 7.50461 39.5348 9.73801 41.2958C11.9714 43.0568 14.7436 43.994 17.5874 43.9495H18.0219C19.8864 43.8697 21.7087 43.3694 23.3526 42.486C24.9964 41.6026 26.4193 40.3589 27.5147 38.848C28.61 37.3371 29.3496 35.598 29.678 33.761C30.0065 31.9239 29.9153 30.0363 29.4112 28.2395C28.9181 26.4723 27.8919 24.8437 26.9937 23.2551C25.4158 20.4653 23.8343 17.6764 22.2492 14.8884C21.7801 14.0647 21.3057 13.2465 20.8419 12.4228C20.2315 11.3353 19.2746 10.1519 19.224 8.86183C19.1733 7.57176 20.2235 6.32701 21.5082 6.07912C23.9284 5.61801 25.0639 8.24078 25.0693 8.23812C25.363 8.94035 25.9123 9.50489 26.6063 9.81764C27.3002 10.1304 28.087 10.168 28.8077 9.92298C29.5283 9.67791 30.1291 9.1684 30.4885 8.49743C30.8479 7.82646 30.9392 7.04405 30.7439 6.30835C30.1514 4.37314 28.9133 2.69953 27.2363 1.56656C25.7615 0.511704 23.9847 -0.0372109 22.1719 0.00195984C20.9049 0.00893199 19.6532 0.27989 18.4967 0.797557C17.3402 1.31522 16.3043 2.06823 15.4551 3.00856C14.49 4.08707 13.7984 5.38193 13.4389 6.78385C13.0794 8.18576 13.0624 9.6536 13.3894 11.0635C13.52 11.593 13.6984 12.1095 13.9225 12.6067C14.5595 14.0514 15.4951 15.3681 16.284 16.7355C17.2525 18.4147 18.2209 20.0948 19.1893 21.7758C20.1578 23.4568 21.1351 25.1449 22.1213 26.8401C22.9209 28.2421 23.7925 29.4682 23.8805 31.1528C23.9175 32.0513 23.7682 32.9479 23.4419 33.7859C23.1156 34.6239 22.6194 35.3854 21.9845 36.0223C21.3496 36.6592 20.5897 37.1578 19.7527 37.4868C18.9157 37.8157 18.0196 37.9678 17.121 37.9336C14.0024 37.7923 11.6488 35.4814 11.1744 32.4588C10.58 28.6419 13.552 26.5469 13.552 26.5469C14.1782 26.1785 14.6497 25.5955 14.8791 24.906C15.1084 24.2166 15.0801 23.4673 14.7993 22.7971C14.5186 22.127 14.0044 21.5813 13.3521 21.2611C12.6998 20.941 11.9536 20.8682 11.2517 21.0561C11.1174 21.0939 10.9856 21.1402 10.8572 21.1947"
+                fill="white"
+            />
+            <path
+                d="M42.8101 31.5968C42.8109 30.5198 42.7218 29.4445 42.5435 28.3823C42.2663 26.7069 41.7464 25.0808 41.0002 23.5552C40.5524 22.6463 39.9874 21.7374 39.1024 21.2417C38.6593 20.9919 38.1589 20.8617 37.6502 20.8639C37.1416 20.8661 36.6423 21.0006 36.2013 21.2541C35.7604 21.5077 35.393 21.8716 35.1352 22.3101C34.8775 22.7485 34.7382 23.2466 34.7312 23.7552C34.7072 24.8773 35.3149 25.8875 35.768 26.9217C36.5212 28.6453 36.8623 30.5208 36.7642 32.3993C36.6661 34.2777 36.1315 36.1075 35.2029 37.7433C35.146 37.8404 35.0952 37.941 35.051 38.0445C34.8623 38.4842 34.7635 38.9573 34.7605 39.4358C34.7802 40.1222 35.0356 40.7808 35.4835 41.3011C35.9315 41.8214 36.5449 42.1717 37.2207 42.2932C38.8759 42.589 40.1899 41.347 40.8856 39.9609C42.1643 37.3589 42.823 34.4961 42.8101 31.5968Z"
+                fill="white"
+            />
+            <path
+                d="M28.2309 11.8938C28.1761 11.9043 28.1218 11.9176 28.0683 11.9338C27.9593 11.9642 27.8611 12.0249 27.7851 12.1088C27.7091 12.1928 27.6584 12.2965 27.6389 12.408C27.6193 12.5195 27.6318 12.6343 27.6748 12.7391C27.7178 12.8438 27.7895 12.9343 27.8818 12.9999C29.2375 14.0252 30.3809 15.3043 31.2482 16.7662C31.4838 17.1677 31.6888 17.5865 31.8612 18.0189C32.0052 18.3921 32.1971 18.8799 32.6822 18.8532C33.0607 18.8346 33.2153 18.512 33.3192 18.1895C33.8137 16.5125 33.9678 14.7534 33.7723 13.0159C33.6331 12.0693 33.4155 11.1359 33.122 10.2252C33.0775 10.0047 32.9744 9.80029 32.8235 9.6335C32.7273 9.54627 32.6054 9.49262 32.4761 9.4806C32.3468 9.46859 32.2171 9.49886 32.1065 9.56687C32.0016 9.65188 31.9115 9.75365 31.8399 9.86806C31.3956 10.4658 30.825 10.9581 30.1687 11.3101C29.8377 11.4861 29.4893 11.6272 29.1292 11.7312C28.828 11.8192 28.5215 11.8325 28.2309 11.8938Z"
+                fill="white"
+            />
+        </svg>
+        Display SwanLab Board
+    </button>
+    <br>
+    <div id="iframeContainer"></div>
+</body>
+</html>
+"""
+
+        display(HTML(total_h5))
+    except ImportError:
+        pass
+
+
 class CloudRunCallback(LocalRunCallback):
     login_info: LoginInfo = None
     """
@@ -51,7 +131,7 @@ def get_login_info(cls):
         """
         key = None
         try:
-            key = get_key(os.path.join(get_swanlab_folder(), ".netrc"), get_host_api())[2]
+            key = get_key()
         except KeyFileError:
             try:
                 fd = sys.stdin.fileno()
@@ -108,7 +188,7 @@ def _except_handler(self, tp, val, tb):
     def __str__(self):
         return "SwanLabCloudRunCallback"
 
-    def on_init(self, project: str, workspace: str, logdir: str = None) -> int:
+    def on_init(self, project: str, workspace: str, logdir: str = None, **kwargs) -> int:
         super(CloudRunCallback, self).on_init(project, workspace, logdir)
         # 检测是否有最新的版本
         self._get_package_latest_version()
@@ -119,6 +199,9 @@ def on_init(self, project: str, workspace: str, logdir: str = None) -> int:
         http = create_http(self.login_info)
         return http.mount_project(project, workspace).history_exp_count
 
+    def before_run(self, settings: SwanLabSharedSettings):
+        self.settings = settings
+
     def on_run(self):
         swanlog.install(self.settings.console_dir)
         # 注册实验信息
diff --git a/swanlab/data/callback_local.py b/swanlab/data/callback_local.py
index 7afed43f..21dccf00 100644
--- a/swanlab/data/callback_local.py
+++ b/swanlab/data/callback_local.py
@@ -7,11 +7,13 @@
 @Description:
     基本回调函数注册表，此时不考虑云端情况
 """
+from swankit.core import SwanLabSharedSettings
 from swanlab.log import swanlog
-from swanlab.utils.font import FONT
 from swanlab.data.run.main import get_run, SwanLabRunState
-from swanlab.data.run.callback import SwanLabRunCallback, MetricInfo, RuntimeInfo
-from swanlab.env import ROOT
+from swanlab.data.run.callback import SwanLabRunCallback
+from swankit.callback import RuntimeInfo, MetricInfo
+from swankit.log import FONT
+from swanlab.env import SwanLabEnv
 from datetime import datetime
 import traceback
 import json
@@ -69,10 +71,10 @@ def _init_logdir(logdir: str = None) -> str:
                 raise ValueError("logdir must be a str.")
             except IOError:
                 raise IOError("logdir must be a path and have Write permission.")
-            os.environ[ROOT] = logdir
+            os.environ[SwanLabEnv.SWANLOG_FOLDER.value] = logdir
         # 如果没有传入logdir，则使用默认的logdir, 即当前工作目录下的swanlog文件夹，但是需要保证目录存在
         else:
-            logdir = os.environ.get(ROOT) or os.path.join(os.getcwd(), "swanlog")
+            logdir = os.environ.get(SwanLabEnv.SWANLOG_FOLDER.value) or os.path.join(os.getcwd(), "swanlog")
             logdir = os.path.abspath(logdir)
             try:
                 os.makedirs(logdir, exist_ok=True)
@@ -107,9 +109,12 @@ def _clean_handler(self):
         # 如果正在运行
         run.finish() if run.running else swanlog.debug("Duplicate finish, ignore it.")
 
-    def on_init(self, proj_name: str, workspace: str, logdir: str = None):
+    def on_init(self, proj_name: str, workspace: str, logdir: str = None, **kwargs):
         self._init_logdir(logdir)
 
+    def before_run(self, settings: SwanLabSharedSettings):
+        self.settings = settings
+
     def on_run(self):
         swanlog.install(self.settings.console_dir)
         # 注入系统回调
diff --git a/swanlab/utils/file.py b/swanlab/data/formater.py
similarity index 58%
rename from swanlab/utils/file.py
rename to swanlab/data/formater.py
index 4182cf63..3eab49df 100644
--- a/swanlab/utils/file.py
+++ b/swanlab/data/formater.py
@@ -1,77 +1,18 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 r"""
-@DATE: 2023-12-02 13:23:42
-@File: swanlab\utils\file.py
-@IDE: vscode
+@DATE: 2024/6/19 14:37
+@File: formater.py
+@IDE: pycharm
 @Description:
-    文件操作
+    入参格式化器
 """
 import os
 import re
-import ujson
+import json
 import yaml
 
 
-# ---------------------------------- 一些格式检查的工具函数 ----------------------------------
-
-
-def is_ipv4(string: str) -> bool:
-    """判断字符串是否是一个ipv4地址
-
-    Parameters
-    ----------
-    string : str
-        待检查的字符串
-
-    Returns
-    -------
-    bool
-        如果是ipv4地址，返回True，否则返回False
-    """
-    pattern = re.compile(r"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$")
-    return isinstance(string, str) and pattern.match(string)
-
-
-def is_port(string: str) -> bool:
-    """判断字符串是否是一个端口号
-
-    Parameters
-    ----------
-    string : str
-        待检查的字符串
-
-    Returns
-    -------
-    bool
-        如果是端口号，返回True，否则返回False
-    """
-    if not is_int(string):
-        return False
-    port = int(string)
-    return 0 <= port <= 65535
-
-
-def is_int(string: str) -> bool:
-    """判断字符串是否可以转换为整数
-
-    Parameters
-    ----------
-    string : str
-        待检查的字符串
-
-    Returns
-    -------
-    bool
-        如果可以转换为整数，返回True，否则返回False
-    """
-    try:
-        int(string)
-        return True
-    except ValueError:
-        return False
-
-
 def check_string(target: str) -> bool:
     """
     检查是否为字符串，且不能全空格，也不能为空字符串
@@ -91,16 +32,17 @@ def check_string(target: str) -> bool:
     return True
 
 
-def check_load_json_yaml(file_path: str, paramter_name: str = "init_path"):
+def check_load_json_yaml(file_path: str, param_name):
     # 不是字符串
     if not isinstance(file_path, str):
-        raise TypeError("{} must be a string, but got {}".format(paramter_name, type(file_path)))
+        raise TypeError("{} must be a string, but got {}".format(param_name, type(file_path)))
     # 检查file_path的后缀是否是json/yaml，否则报错
     path_suffix = file_path.split(".")[-1]
     if not file_path.endswith((".json", ".yaml", ".yml")):
         raise ValueError(
-            "{} must be a json or yaml file ('.json', '.yaml', '.yml'), but got {}, please check if the content of config_file is correct.".format(
-                paramter_name, path_suffix
+            "{} must be a json or yaml file ('.json', '.yaml', '.yml'), "
+            "but got {}, please check if the content of config_file is correct.".format(
+                param_name, path_suffix
             )
         )
     # 转换为绝对路径
@@ -108,16 +50,16 @@ def check_load_json_yaml(file_path: str, paramter_name: str = "init_path"):
     # 读取配置文件
     # 如果文件不存在或者不是文件
     if (not os.path.exists(file_path)) or (not os.path.isfile(file_path)):
-        raise FileNotFoundError("{} not found, please check if the file exists.".format(paramter_name))
+        raise FileNotFoundError("{} not found, please check if the file exists.".format(param_name))
     # 为空
     if os.path.getsize(file_path) == 0:
-        raise ValueError("{} is empty, please check if the content of config_file is correct.".format(paramter_name))
+        raise ValueError("{} is empty, please check if the content of config_file is correct.".format(param_name))
     # 无权限读取
     if not os.access(file_path, os.R_OK):
         raise PermissionError(
-            "No permission to read {}, please check if you have the permission.".format(paramter_name)
+            "No permission to read {}, please check if you have the permission.".format(param_name)
         )
-    load = ujson.load if path_suffix == "json" else yaml.safe_load
+    load = json.load if path_suffix == "json" else yaml.safe_load
     with open(file_path, "r") as f:
         # 读取配置文件的内容
         file_data = load(f)
@@ -182,67 +124,6 @@ def check_proj_name_format(name: str, auto_cut: bool = True) -> str:
     return _auto_cut("project", name, max_len, auto_cut)
 
 
-def check_exp_name_format(name: str, auto_cut: bool = True) -> str:
-    """
-    检查实验名称格式，最大长度为100个字符，一个中文字符算一个字符
-    其他不做限制，实验名称可以包含任何字符
-
-    Parameters
-    ----------
-    name : str
-        待检查的字符串
-    auto_cut : bool, optional
-        如果超出长度，是否自动截断，默认为True
-        如果为False，则超出长度会抛出异常
-
-    Returns
-    -------
-    str
-        检查后的字符串
-
-    Raises
-    ------
-    TypeError
-        name不是字符串
-    ValueError
-        name为空字符串
-    IndexError
-        name超出长度
-    """
-    max_len = 100
-    if not check_string(name):
-        raise ValueError(f"Experiment name `{name}` is an empty string.")
-    # 检查长度
-    return _auto_cut("experiment", name, max_len, auto_cut)
-
-
-def check_desc_format(description: str, auto_cut: bool = True):
-    """检查实验描述
-    不能超过255个字符，可以包含任何字符
-
-    Parameters
-    ----------
-    description : str
-        需要检查和处理的描述信息
-    auto_cut : bool
-        如果超出长度，是否裁剪并抛弃多余部分
-
-    Returns
-    -------
-    str
-        检查后的字符串，同时会去除字符串头尾的空格
-
-    Raises
-    ------
-    IndexError
-        name超出长度
-    """
-    max_length = 255
-    check_string(description)
-    description = description.strip()
-    return _auto_cut("description", description, max_length, auto_cut)
-
-
 def check_key_format(key: str, auto_cut=True) -> str:
     """检查key字符串格式
     不能超过255个字符，可以包含任何字符，不允许.和/以及空格开头
diff --git a/swanlab/data/modules/__init__.py b/swanlab/data/modules/__init__.py
index adfe28eb..0ea08e42 100644
--- a/swanlab/data/modules/__init__.py
+++ b/swanlab/data/modules/__init__.py
@@ -1,10 +1,10 @@
-from .base import BaseType, MediaType, ParseResult, MediaBuffer
 from .audio import Audio
 from .image import Image
 from .text import Text
 from .line import Line, FloatConvertible
 from typing import Union, List
-from .wrapper import DataWrapper, WrapperErrorInfo
+from .wrapper import DataWrapper
+from swankit.core.data import MediaType, MediaBuffer, BaseType
 
 DataType = Union[int, float, FloatConvertible, BaseType, List[BaseType]]
 ChartType = BaseType.Chart
@@ -19,6 +19,5 @@
     "Line",
     "DataType",
     "ChartType",
-    "WrapperErrorInfo",
     "MediaBuffer"
 ]
diff --git a/swanlab/data/modules/audio/__init__.py b/swanlab/data/modules/audio/__init__.py
index c295d7ed..d63cce37 100644
--- a/swanlab/data/modules/audio/__init__.py
+++ b/swanlab/data/modules/audio/__init__.py
@@ -7,20 +7,26 @@
 @Description:
     音频模块
 """
-from ..base import MediaType, MediaBuffer, DataSuite as D
+from swankit.core import MediaBuffer, DataSuite as D, MediaType
 from typing import Union
-import soundfile as sf
-import numpy as np
+
+try:
+    # noinspection PyPackageRequirements
+    import soundfile as sf
+    # noinspection PyPackageRequirements
+    import numpy as np
+except ImportError:
+    sf, np = None, None
 
 
 class Audio(MediaType):
     SF_SUPPORT_DTYPE = [np.dtype(d) for d in ["float32", "float64", "int16", "int32"]]
 
     def __init__(
-        self,
-        data_or_path: Union[str, np.ndarray],
-        sample_rate: int = 44100,
-        caption: str = None,
+            self,
+            data_or_path: Union[str, np.ndarray],
+            sample_rate: int = 44100,
+            caption: str = None,
     ):
         """Audio class constructor
 
@@ -33,6 +39,11 @@ def __init__(
         caption: str
             Caption for the audio.
         """
+        if sf is None or np is None:
+            raise ImportError(
+                "soundfile and numpy are required for Audio class, "
+                'you can install them by `pip install "swanlab[media]"`'
+            )
         super().__init__()
         if isinstance(data_or_path, str):
             # 如果输入为路径字符串
diff --git a/swanlab/data/modules/base.py b/swanlab/data/modules/base.py
deleted file mode 100644
index ae21be20..00000000
--- a/swanlab/data/modules/base.py
+++ /dev/null
@@ -1,295 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2023-12-28 22:12:37
-@File: swanlab/database/modules/base.py
-@IDE: vscode
-@Description:
-    SwanLab数据基类
-    数据的转换流程是：
-    1. 如果传入的数据在这之前已经有key，跳转步骤3
-    2. 没有key的数据，需要判断当前数据是否满足要求，因此需要转换一次，跳转步骤3
-    3. 转换一次，判断与之前的数据是否一致，因此还需要转换一次
-    可以看到有两次转换，如果每次都是耗时操作，那么会导致性能问题，所以对于单个类而言，上次转换的结果会被保存
-"""
-from abc import ABC, abstractmethod
-from typing import List, Dict, Optional, ByteString, Union, Tuple
-from swanlab.log import swanlog
-from enum import Enum
-from io import BytesIO
-import hashlib
-import math
-import io
-
-
-class DataSuite:
-    """
-    BaseType在处理时的通用工具套件
-    """
-
-    @staticmethod
-    def get_hash_by_bytes(file_byte: ByteString, chunk: int = 4096) -> str:
-        """
-        计算并返回给定字节流的SHA-256哈希值
-        :param file_byte: 字节流
-        :param chunk: 一次读取的字节数，用于加快处理速度
-        """
-
-        hash_sha256 = hashlib.sha256()
-        # 更新哈希值，为了处理速度，只读取一小块
-        hash_sha256.update(file_byte[:chunk])
-        return hash_sha256.hexdigest()
-
-    @staticmethod
-    def get_hash_by_ndarray(array) -> str:
-        """
-        计算并返回给定NumPy数组的SHA-256哈希值
-        :param array: NumPy数组
-        """
-
-        hash_sha256 = hashlib.sha256()
-        # 将NumPy数组转换为字节串，然后更新哈希值
-        hash_sha256.update(array.tobytes())
-        return hash_sha256.hexdigest()
-
-    @staticmethod
-    def get_hash_by_pil(image, f="PNG") -> str:
-        """计算并返回给定PIL.Image对象的SHA-256哈希值。
-        :param image: PIL.Image对象
-        :param f: 图像格式，默认为PNG，可选为JPEG
-        """
-
-        hash_sha256 = hashlib.sha256()
-        # 将图像转换为字节数据
-        with io.BytesIO() as buffer:
-            image.save(buffer, format=f)
-            hash_sha256.update(buffer.getvalue())
-        return hash_sha256.hexdigest()
-
-    @staticmethod
-    def is_nan(data):
-        """判断data是否为nan"""
-        return isinstance(data, (int, float)) and math.isnan(data)
-
-    @staticmethod
-    def is_inf(data):
-        """判断data是否为inf"""
-        return isinstance(data, (int, float)) and math.isinf(data)
-
-    @staticmethod
-    def check_caption(caption) -> Optional[str]:
-        """
-        检查caption是否符合要求
-        :param caption: 标题
-        :return: 返回处理后的标题
-        :raises TypeError: caption不符合要求
-        """
-        if isinstance(caption, str):
-            caption = caption
-        # 如果类型是数字，则转换为字符串
-        elif isinstance(caption, (int, float)):
-            caption = str(caption)
-        # 如果类型是None，则转换为默认字符串
-        elif caption is None:
-            caption = None
-        else:
-            raise TypeError("caption must be a string, int or float.")
-        return caption.strip() if caption else None
-
-
-class DynamicProperty:
-    """
-    动态属性，一开始为None，动态设置
-    但是可以保证在转换时都已经被设置
-    """
-
-    def __init__(self):
-        # 保存的key
-        self.key: Optional[str] = None
-        # 保存的step
-        self.step: Optional[int] = None
-
-    def inject(self, key: str, step: int):
-        """
-        注入属性
-        :param key: key名称
-        :param step: 当前步骤
-        """
-        self.key = key
-        self.step = step
-
-
-class BaseType(ABC, DynamicProperty):
-    """
-    SwanLab数据基类，完成数据解析，拿到图表类型、namespace和解析后的数据
-    所有的数据类都只负责解析，并返回相应的内容
-    """
-
-    class Chart(Enum):
-        """
-        图表选择器
-        """
-
-        class ChartItem:
-            """
-            图表项
-            """
-
-            def __init__(self, chart_type: str, column_type: str):
-                self.chart_type = chart_type
-                """
-                本地标注的图表类型
-                """
-                self.column_type = column_type
-                """
-                上传列信息时，标注的图表类型
-                """
-
-        LINE = ChartItem("line", "FLOAT")
-
-        IMAGE = ChartItem("image", "IMAGE")
-
-        AUDIO = ChartItem("audio", "AUDIO")
-
-        TEXT = ChartItem("text", "TEXT")
-
-    # ---------------------------------- 需要子类实现的方法 ----------------------------------
-
-    @abstractmethod
-    def parse(self) -> Tuple[Union[str, float], Optional[BytesIO]]:
-        """
-        解析数据
-        :raises DataTypeError: 传入的数据类型不符合要求
-        """
-        pass
-
-    @abstractmethod
-    def get_chart(self) -> "Chart":
-        """
-        获取图表类型
-        """
-        pass
-
-    # ---------------------------------- 可选覆写方法 ----------------------------------
-    # noinspection PyMethodMayBeStatic
-    def get_section(self) -> str:
-        """
-        获取默认的section名称，可能在后续处理时，与实际的section名称不同
-        """
-        return "default"
-
-    # noinspection PyMethodMayBeStatic
-    def get_config(self) -> Optional[Dict]:
-        """
-        获取图表的config配置信息，应该返回一个字典，或者为None
-        """
-        return None
-
-    def get_more(self) -> Optional[Dict]:
-        """
-        代表当前步骤的此数据支持标注的更多内容，应该返回一个字典，或者为None
-        """
-        return None
-
-
-class MediaType(BaseType):  # noqa
-    """
-    媒体类型，用于区分标量和媒体，不用做实例化，应该由子类继承
-    """
-    pass
-
-
-class MediaBuffer(BytesIO):
-    def __init__(self):
-        super().__init__()
-        self.__file_name = None
-
-    @property
-    def file_name(self):
-        if self.__file_name is None:
-            raise ValueError("file_name is not set")
-        return self.__file_name
-
-    @file_name.setter
-    def file_name(self, value):
-        if not isinstance(value, str) or not value:
-            raise TypeError(f"Expected str, but got {type(value)}")
-        # if self.__file_name is not None:
-        #     # 此时意味着使用类似 [] * 2 的操作复制了多个相同的实例，这允许，但不推荐
-        #     swanlog.warning("You are logging a duplicate and same instance, this is not recommended")
-        self.__file_name = value
-
-
-class ParseResult:
-    """
-    数据解析结果
-    """
-
-    def __init__(
-        self,
-        section: str = None,
-        chart: BaseType.Chart = None,
-        data: Union[List[str], float] = None,
-        config: Optional[List[Dict]] = None,
-        more: Optional[List[Dict]] = None,
-        buffers: Optional[List[MediaBuffer]] = None,
-    ):
-        """
-        :param section: 转换后数据对应的section
-        :param chart: 转换后数据对应的图表类型，枚举类型
-        :param data: 存储在.log中的数据
-        :param config: 存储在.log中的配置
-        :param more: 存储在.log中的更多信息
-        :param buffers: 存储于media文件夹中的原始数据，比特流，特别的，对于某些字符串即原始数据的情况，此处为None
-        """
-        self.__data = data
-        self.config = config
-        self.more = more
-        self.buffers = buffers
-        self.section = section
-        self.chart = chart
-        # 默认的reference
-        self.reference = "step"
-        self.step = None
-
-    @property
-    def data(self):
-        """
-        获取数据，如果不确定为何种数据类型，可以使用此属性
-        """
-        return self.__data
-
-    @property
-    def float(self):
-        """
-        当确认数据为float时，可以使用此属性明确为float
-        """
-        return self.__data
-
-    @float.setter
-    def float(self, value):
-        if not isinstance(value, (int, float)):
-            # 无法被nan或者inf表示的数据，不允许被设置为float
-            if not (math.isnan(float(value)) or math.isinf(float(value))):
-                raise TypeError(f"Expected float, but got {type(value)}")
-            elif math.isnan(float(value)):
-                swanlog.warning(f"Your are setting a nan to float")
-            else:
-                swanlog.warning(f"Your are setting a inf to float")
-        self.__data = value
-
-    @property
-    def strings(self):
-        """
-        当确认数据为字符串时，可以使用此属性明确为字符串
-        """
-        return self.__data
-
-    @strings.setter
-    def strings(self, value):
-        if not isinstance(value, list) or not all(isinstance(i, str) for i in value):
-            raise TypeError(f"Expected list(str), but got {type(value)}")
-        # 不允许设置为空列表，目的是希望在设置时就能保证数据的完整性
-        if not len(value):
-            raise ValueError("Expected list(str) with at least one element")
-        self.__data = value
diff --git a/swanlab/data/modules/image/__init__.py b/swanlab/data/modules/image/__init__.py
index 2a2e0123..1af464a9 100644
--- a/swanlab/data/modules/image/__init__.py
+++ b/swanlab/data/modules/image/__init__.py
@@ -1,10 +1,17 @@
-import numpy as np
-from PIL import Image as PILImage
-from matplotlib import pyplot as plt
-from ..base import MediaType, MediaBuffer, DataSuite as D
+from swankit.core import MediaBuffer, DataSuite as D, MediaType
 from typing import Union, Any
 from io import BytesIO
 
+try:
+    # noinspection PyPackageRequirements
+    import numpy as np
+    # noinspection PyPackageRequirements
+    from PIL import Image as PILImage
+    # noinspection PyPackageRequirements
+    from matplotlib import pyplot as plt
+except ImportError:
+    np, PILImage, plt = None, None, None
+
 
 def is_pytorch_tensor_typename(typename: str) -> bool:
     return typename.startswith("torch.") and ("Tensor" in typename or "Variable" in typename)
@@ -44,12 +51,12 @@ class Image(MediaType):
     ACCEPT_FORMAT = ["png", "jpg", "jpeg", "bmp"]
 
     def __init__(
-        self,
-        data_or_path: Union[str, np.ndarray, PILImage.Image, plt.plot],
-        mode: str = None,
-        caption: str = None,
-        file_type: str = None,
-        size: Union[int, list, tuple] = None,
+            self,
+            data_or_path: Union[str, np.ndarray, PILImage.Image, plt.plot],
+            mode: str = None,
+            caption: str = None,
+            file_type: str = None,
+            size: Union[int, list, tuple] = None,
     ):
         """Image class constructor
 
@@ -81,7 +88,11 @@ def __init__(
 
             If it is None, it means no scaling for the image.
         """
-
+        if PILImage is None or np is None or plt is None:
+            raise ImportError(
+                "pillow、numpy and matplotlib are required for Image class, "
+                'you can install them by `pip install "swanlab[media]"`'
+            )
         super().__init__()
         self.format = self.__convert_file_type(file_type)
         self.size = convert_size(size)
diff --git a/swanlab/data/modules/line/__init__.py b/swanlab/data/modules/line/__init__.py
index af7af586..41ca03c6 100644
--- a/swanlab/data/modules/line/__init__.py
+++ b/swanlab/data/modules/line/__init__.py
@@ -14,7 +14,7 @@
 """
 from swanlab.error import DataTypeError
 from typing import Protocol, runtime_checkable
-from ..base import BaseType, DataSuite as D
+from swankit.core import BaseType, DataSuite as D
 
 
 @runtime_checkable
diff --git a/swanlab/data/modules/text/__init__.py b/swanlab/data/modules/text/__init__.py
index 36309f7c..bfa24589 100644
--- a/swanlab/data/modules/text/__init__.py
+++ b/swanlab/data/modules/text/__init__.py
@@ -7,7 +7,8 @@
 @Description:
     文本模块
 """
-from ..base import MediaType, DataSuite as D
+from swankit.core.data import MediaType
+from swankit.core import DataSuite as D
 from typing import Union
 
 
diff --git a/swanlab/data/modules/wrapper.py b/swanlab/data/modules/wrapper.py
index b5765fd5..82e471f6 100644
--- a/swanlab/data/modules/wrapper.py
+++ b/swanlab/data/modules/wrapper.py
@@ -7,46 +7,12 @@
 @Description:
     包装器
 """
-from .base import BaseType, MediaType, ParseResult
+from swankit.core import ParseResult, ParseErrorInfo, MediaType
 from typing import Union, List, Optional
 from swanlab.error import DataTypeError
 from .line import Line
 
 
-class WrapperErrorInfo:
-    """
-    DataWrapper转换时的错误信息
-    """
-
-    def __init__(
-        self,
-        expected: Optional[str],
-        got: Optional[str],
-        chart: Optional[BaseType.Chart],
-        duplicated: bool = False
-    ):
-        """
-        :param expected: 期望的数据类型
-        :param got: 实际的数据类型
-        :param chart: 当前错误数据对应的图表类型
-        :param duplicated: 是否是重复错误,如果为是，expected和got和chart都为None
-        """
-        self.expected = expected if not duplicated else None
-        self.got = got if not duplicated else None
-        self.chart = chart if not duplicated else None
-        self.__duplicated = duplicated
-
-    def dict(self):
-        return {"expected": self.expected, "got": self.got}
-
-    @property
-    def duplicated(self) -> bool:
-        """
-        是否是重复错误，重复错误时，got和expected为None
-        """
-        return self.__duplicated
-
-
 class DataWrapper:
     """
     数据包装器，负责解析数据，返回相应的数据类型
@@ -91,7 +57,7 @@ def parsed(self) -> bool:
         return self.__result is not None or self.__error is not None
 
     @property
-    def error(self) -> Optional[WrapperErrorInfo]:
+    def error(self) -> Optional[ParseErrorInfo]:
         """
         解析时候的错误信息
         """
@@ -114,13 +80,13 @@ def parse(self, **kwargs) -> Optional[ParseResult]:
         # [Line]
         if self.type == Line:
             if len(self.__data) > 1:
-                self.__error = WrapperErrorInfo("float", "list(Line)", result.chart)
+                self.__error = ParseErrorInfo("float", "list(Line)", result.chart)
             else:
                 d.inject(**kwargs)
                 try:
                     result.float, _ = d.parse()
                 except DataTypeError as e:
-                    self.__error = WrapperErrorInfo(e.expected, e.got, result.chart)
+                    self.__error = ParseErrorInfo(e.expected, e.got, result.chart)
             self.__result = result
             return self.__result
 
@@ -132,7 +98,7 @@ def parse(self, **kwargs) -> Optional[ParseResult]:
                 i.inject(**kwargs)
                 d, r = i.parse()
             except DataTypeError as e:
-                self.__error = WrapperErrorInfo(e.expected, e.got, result.chart)
+                self.__error = ParseErrorInfo(e.expected, e.got, result.chart)
                 return None
             data.append(d)
             buffers.append(r)
@@ -156,8 +122,8 @@ def __filter_list(li: List):
         return None
 
     @classmethod
-    def create_duplicate_error(cls) -> WrapperErrorInfo:
+    def create_duplicate_error(cls) -> ParseErrorInfo:
         """
         快捷创建一个重复错误
         """
-        return WrapperErrorInfo(None, None, None, duplicated=True)
+        return ParseErrorInfo(None, None, None, duplicated=True)
diff --git a/swanlab/data/run/callback/utils.py b/swanlab/data/run/callback.py
similarity index 57%
rename from swanlab/data/run/callback/utils.py
rename to swanlab/data/run/callback.py
index b11d0765..3e3ee3fe 100644
--- a/swanlab/data/run/callback/utils.py
+++ b/swanlab/data/run/callback.py
@@ -1,18 +1,21 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 r"""
-@DATE: 2024/6/5 16:34
-@File: utils.py
+@DATE: 2024/6/19 16:46
+@File: callback.py
 @IDE: pycharm
 @Description:
-    工具类
+    回调函数注册抽象模块
 """
-from typing import Optional, Any
-from swanlab.data.run.settings import SwanDataSettings
+from typing import Optional
+from swankit.core import SwanLabSharedSettings
+from swankit.callback import SwanKitCallback
 from swanlab.log import swanlog
-from swanlab.utils.font import FONT
+from swankit.log import FONT
 from swanlab.env import is_windows
 from swanlab.package import get_package_version
+import atexit
+import sys
 import os
 
 
@@ -22,15 +25,7 @@ class U:
     """
 
     def __init__(self):
-        self.settings: Optional[SwanDataSettings] = None
-
-    def inject(self, settings: SwanDataSettings):
-        """
-        为SwanLabRunCallback注入settings等一些依赖，因为实例化可能在SwanLabRun之前发生
-        :param settings: SwanDataSettings, 数据配置
-        :return:
-        """
-        self.settings = settings
+        self.settings: Optional[SwanLabSharedSettings] = None
 
     @staticmethod
     def fmt_windows_path(path: str) -> str:
@@ -84,3 +79,43 @@ def _train_finish_print(self):
         打印结束信息
         """
         swanlog.info("Experiment {} has completed".format(FONT.yellow(self.settings.exp_name)))
+
+
+class SwanLabRunCallback(SwanKitCallback, U):
+    """
+    SwanLabRunCallback，回调函数注册类，所有以`on_`和`before_`开头的函数都会在对应的时机被调用
+    为了方便管理：
+    1. `_`开头的函数为内部函数，不会被调用，且写在最开头
+    2. 所有回调按照逻辑上的触发顺序排列
+    3. 带有from_*后缀的回调函数代表调用者来自其他地方，比如config、operator等，这将通过settings对象传递
+    4. 所有回调不要求全部实现，只需实现需要的回调即可
+    """
+
+    def _register_sys_callback(self):
+        """
+        注册系统回调，内部使用
+        """
+        sys.excepthook = self._except_handler
+        atexit.register(self._clean_handler)
+
+    def _unregister_sys_callback(self):
+        """
+        注销系统回调，内部使用
+        """
+        sys.excepthook = sys.__excepthook__
+        atexit.unregister(self._clean_handler)
+
+    def _clean_handler(self):
+        """
+        正常退出清理函数，此函数调用`run.finish`
+        """
+        pass
+
+    def _except_handler(self, tp, val, tb):
+        """
+        异常退出清理函数
+        """
+        pass
+
+    def __str__(self):
+        raise NotImplementedError("Please implement this method")
diff --git a/swanlab/data/run/callback/__init__.py b/swanlab/data/run/callback/__init__.py
deleted file mode 100644
index 7d1e443c..00000000
--- a/swanlab/data/run/callback/__init__.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/5 16:27
-@File: __init__.py
-@IDE: pycharm
-@Description:
-    回调函数注册抽象模块
-"""
-from typing import Callable
-from abc import ABC, abstractmethod
-from .utils import U
-from .models import *
-import atexit
-import sys
-
-
-class SwanLabRunCallback(ABC, U):
-    """
-    SwanLabRunCallback，回调函数注册类，所有以`on_`和`before_`开头的函数都会在对应的时机被调用
-    为了方便管理：
-    1. `_`开头的函数为内部函数，不会被调用，且写在最开头
-    2. 所有回调按照逻辑上的触发顺序排列
-    3. 带有from_*后缀的回调函数代表调用者来自其他地方，比如config、operator等，这将通过settings对象传递
-    4. 所有回调不要求全部实现，只需实现需要的回调即可
-    """
-
-    def _register_sys_callback(self):
-        """
-        注册系统回调，内部使用
-        """
-        sys.excepthook = self._except_handler
-        atexit.register(self._clean_handler)
-
-    def _unregister_sys_callback(self):
-        """
-        注销系统回调，内部使用
-        """
-        sys.excepthook = sys.__excepthook__
-        atexit.unregister(self._clean_handler)
-
-    def _clean_handler(self):
-        """
-        正常退出清理函数，此函数调用`run.finish`
-        """
-        pass
-
-    def _except_handler(self, tp, val, tb):
-        """
-        异常退出清理函数
-        """
-        pass
-
-    def on_init(self, proj_name: str, workspace: str, logdir: str = None):
-        """
-        执行`swanlab.init`时调用
-        此时运行时环境变量没有被设置，此时修改环境变量还是有效的
-        :param logdir: str, 用户设置的日志目录
-        :param proj_name: str, 项目名称
-        :param workspace: str, 工作空间
-        """
-        pass
-
-    def before_init_experiment(
-        self,
-        run_id: str,
-        exp_name: str,
-        description: str,
-        num: int,
-        suffix: str,
-        setter: Callable[[str, str, str, str], None],
-    ):
-        """
-        在初始化实验之前调用，此时SwanLabRun已经初始化完毕
-        :param run_id: str, SwanLabRun的运行id
-        :param exp_name: str, 实验名称
-        :param description: str, 实验描述
-        :param num: int, 历史实验数量
-        :param suffix: str, 实验后缀
-        :param setter: Callable[[str, str, str, str], None], 设置实验信息的函数，在这里设置实验信息
-        """
-        pass
-
-    def on_run(self):
-        """
-        SwanLabRun初始化完毕时调用
-        """
-        pass
-
-    def on_run_error_from_operator(self, e: OperateErrorInfo):
-        """
-        SwanLabRun初始化错误时被操作员调用
-        """
-        pass
-
-    def on_runtime_info_update(self, r: RuntimeInfo):
-        """
-        运行时信息更新时调用
-        :param r: RuntimeInfo, 运行时信息
-        """
-        pass
-
-    def on_log(self):
-        """
-        每次执行swanlab.log时调用
-        """
-        pass
-
-    def on_column_create(self, column_info: ColumnInfo):
-        """
-        列创建回调函数,新增列信息时调用
-        """
-        pass
-
-    def on_metric_create(self, metric_info: MetricInfo):
-        """
-        指标创建回调函数,新增指标信息时调用
-        """
-        pass
-
-    def on_stop(self, error: str = None):
-        """
-        训练结束时的回调函数
-        """
-        pass
-
-    @abstractmethod
-    def __str__(self) -> str:
-        """
-        返回当前回调函数的名称，这条应该是一个全局唯一的标识
-        在operator中会用到这个名称，必须唯一
-        """
-        pass
diff --git a/swanlab/data/run/callback/models/__init__.py b/swanlab/data/run/callback/models/__init__.py
deleted file mode 100644
index 7ff7f991..00000000
--- a/swanlab/data/run/callback/models/__init__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/5 16:29
-@File: __init__.py
-@IDE: pycharm
-@Description:
-    与回调函数通信时的模型
-"""
-from .key import MediaBuffer, MetricInfo, ColumnInfo
-from .error import OperateErrorInfo
-from .runtime import RuntimeInfo
-
-__all__ = ["MediaBuffer", "MetricInfo", "ColumnInfo", "OperateErrorInfo", "RuntimeInfo"]
diff --git a/swanlab/data/run/callback/models/error.py b/swanlab/data/run/callback/models/error.py
deleted file mode 100644
index 332624ba..00000000
--- a/swanlab/data/run/callback/models/error.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/5 16:30
-@File: error.py
-@IDE: pycharm
-@Description:
-    错误模型
-"""
-
-
-class OperateErrorInfo:
-    """
-    操作错误信息，当操作员操作回调发生错误时，会生成这个对象，传给相应的回调函数
-    """
-
-    def __init__(self, desc: str):
-        self.desc = desc
-        """
-        错误描述
-        """
-
-    def __str__(self):
-        return f"SwanLabError: {self.desc}"
diff --git a/swanlab/data/run/callback/models/key.py b/swanlab/data/run/callback/models/key.py
deleted file mode 100644
index 06d33861..00000000
--- a/swanlab/data/run/callback/models/key.py
+++ /dev/null
@@ -1,184 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/5 16:32
-@File: key.py
-@IDE: pycharm
-@Description:
-    与Key相关的回调函数触发时的模型
-"""
-from typing import Union, Optional, Dict, List
-from swanlab.data.modules import ChartType, WrapperErrorInfo, MediaBuffer
-from urllib.parse import quote
-import os
-
-
-class ColumnInfo:
-    """
-    列信息，当创建列时，会生成这个对象
-    """
-
-    def __init__(
-        self,
-        key: str,
-        namespace: str,
-        chart: ChartType,
-        sort: Optional[int] = None,
-        error: Optional[WrapperErrorInfo] = None,
-        reference: Optional[str] = None,
-        config: Optional[Dict] = None,
-    ):
-        self.key = key
-        """
-        列的key名称
-        """
-        self.namespace = namespace
-        """
-        列的命名空间
-        """
-        self.chart = chart
-        """
-        列的图表类型
-        """
-        self.error = error
-        """
-        列的类型错误信息
-        """
-        self.reference = reference
-        """
-        列的参考对象
-        """
-        self.sort = sort
-        """
-        列在namespace中的排序
-        """
-        self.config = config if config is not None else {}
-        """
-        列的额外配置信息
-        """
-
-    @property
-    def got(self):
-        """
-        传入的错误类型，如果列出错，返回错误类型，如果没出错，`暂时`返回None
-        """
-        if self.error is None:
-            return None
-        return self.error.got
-
-    @property
-    def expected(self):
-        """
-        期望的类型，如果列出错，返回期望的类型，如果没出错，`暂时`返回None
-        """
-        if self.error is None:
-            return None
-        return self.error.expected
-
-
-class MetricInfo:
-    """
-    指标信息，当新的指标被log时，会生成这个对象
-    """
-    __SUMMARY_NAME = "_summary.json"
-
-    def __init__(
-        self,
-        key: str,
-        column_info: ColumnInfo,
-        error: Optional[WrapperErrorInfo],
-        metric: Union[Dict, None] = None,
-        summary: Union[Dict, None] = None,
-        step: int = None,
-        epoch: int = None,
-        logdir: str = None,
-        metric_file_name: str = None,
-        media_dir: str = None,
-        buffers: List[MediaBuffer] = None,
-    ):
-        self.__error = error
-
-        self.key = quote(key, safe="")
-        """
-        指标的key名称，被quote编码
-        """
-        self.column_info = column_info
-        """
-        指标对应的列信息
-        """
-        self.metric = metric
-        """
-        指标信息，error时为None
-        """
-        self.summary = summary
-        """
-        摘要信息，error时为None
-        """
-        self.step = step
-        """
-        当前指标的步数，error时为None
-        """
-        self.epoch = epoch
-        """
-        当前指标对应本地的行数，error时为None
-        """
-        self.metric_path = None if self.error else os.path.join(logdir, self.key, metric_file_name)
-        """
-        指标文件的路径，error时为None
-        """
-        self.summary_path = None if self.error else os.path.join(logdir, self.key, self.__SUMMARY_NAME)
-        """
-        摘要文件的路径，error时为None
-        """
-        self.media_dir = media_dir
-        """
-        静态文件的根文件夹
-        """
-        self.buffers = buffers
-        """
-        需要上传的媒体数据，比特流，error时为None，如果上传为非媒体类型（或Text类型），也为None
-        """
-        # 写入文件名称，对应上传时的文件名称
-        if self.buffers is not None:
-            for i, buffer in enumerate(self.buffers):
-                buffer.file_name = "{}/{}".format(self.key, metric["data"][i])
-
-    @property
-    def error(self) -> bool:
-        """
-        这条指标信息是否有错误，错误分几种：
-            1. 列错误，列一开始就出现问题
-            2. 重复错误
-            3. 指标错误
-        """
-        return self.error_info is not None or self.column_error_info is not None
-
-    @property
-    def column_error_info(self) -> Optional[WrapperErrorInfo]:
-        """
-        列错误信息
-        """
-        return self.column_info.error
-
-    @property
-    def error_info(self) -> Optional[WrapperErrorInfo]:
-        """
-        指标错误信息
-        """
-        return self.__error
-
-    @property
-    def duplicated_error(self) -> bool:
-        """
-        是否是重复的指标
-        """
-        return self.__error and self.__error.duplicated
-
-    @property
-    def data(self) -> Union[Dict, None]:
-        """
-        指标数据的data字段
-        """
-        if self.error:
-            return None
-        return self.metric["data"]
diff --git a/swanlab/data/run/callback/models/runtime.py b/swanlab/data/run/callback/models/runtime.py
deleted file mode 100644
index dbfb55de..00000000
--- a/swanlab/data/run/callback/models/runtime.py
+++ /dev/null
@@ -1,143 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/5 16:55
-@File: runtime.py
-@IDE: pycharm
-@Description:
-    运行时信息模型
-"""
-from abc import ABC, abstractmethod
-from typing import Optional, Any
-import json
-import yaml
-import os
-
-
-class InfoWriter(ABC):
-
-    def __init__(self, info):
-        self.info = info
-
-    @abstractmethod
-    def write(self, path: str):
-        """
-        写入到本地文件的方法
-        :param path: 写入的路径，不包含文件名
-        """
-        pass
-
-    @abstractmethod
-    def dumps(self):
-        """
-        将信息转换为字符串
-        """
-        pass
-
-    @abstractmethod
-    def to_dict(self):
-        """
-        将信息转换为字典
-        """
-        pass
-
-
-class RequirementInfo(InfoWriter):
-    """
-    依赖信息
-    """
-
-    def __init__(self, info: str):
-        super().__init__(info)
-        self.name = "requirements.txt"
-
-    def write(self, path: str):
-        with open(os.path.join(path, self.name), "w", encoding="utf-8") as f:
-            f.write(self.info)
-
-    def dumps(self):
-        return self.info
-
-    def to_dict(self):
-        raise NotImplementedError("RequirementInfo has no to_dict method")
-
-
-class MetadataInfo(InfoWriter):
-    """
-    系统信息
-    """
-
-    def __init__(self, info: dict):
-        super().__init__(info)
-        self.name = "swanlab-metadata.json"
-
-    def write(self, path: str):
-        with open(os.path.join(path, self.name), "w", encoding="utf-8") as f:
-            f.write(self.dumps())
-
-    def dumps(self):
-        return json.dumps(self.to_dict(), ensure_ascii=False)
-
-    def to_dict(self):
-        return self.info
-
-
-class ConfigInfo(InfoWriter):
-    """
-    配置信息
-    """
-
-    def __init__(self, info: dict):
-        super().__init__(info)
-        self.name = "config.yaml"
-        self.__data = None
-
-    def write(self, path: str):
-        with open(os.path.join(path, self.name), "w", encoding="utf-8") as f:
-            f.write(self.dumps())
-
-    def dumps(self):
-        return yaml.dump(self.to_dict(), allow_unicode=True)
-
-    def to_dict(self):
-        """
-        返回配置信息的字典形式
-        """
-        # 遍历每一个配置项，值改为value，增加desc和sort字段
-        # 原因是序列化时可能会丢失key的排序信息，所以这里增加一个sort字段
-        # 没有在__init__中直接修改是因为可能会有其他地方需要原始数据，并且会丢失一些性能
-        if self.__data is not None:
-            return self.__data
-        self.__data = {
-            k: {
-                "value": v,
-                "sort": i,
-                "desc": ""
-            } for i, (k, v) in enumerate(self.info.items())
-        }
-        return self.__data
-
-
-class RuntimeInfo:
-    """
-    运行时信息，包括系统信息，依赖信息等
-    如果某些信息为None，代表没有传入配置
-    """
-
-    def __init__(self, requirements: str = None, metadata: dict = None, config: dict = None):
-        """
-        :param requirements: python依赖信息
-        :param metadata: 系统信息
-        :param config: 上传的配置信息
-        """
-        self.requirements: Optional[RequirementInfo] = RequirementInfo(
-            requirements
-        ) if requirements is not None else None
-
-        self.metadata: Optional[MetadataInfo] = MetadataInfo(
-            metadata
-        ) if metadata is not None else None
-
-        self.config: Optional[ConfigInfo] = ConfigInfo(
-            config
-        ) if config is not None else None
diff --git a/swanlab/data/run/config.py b/swanlab/data/run/config.py
index a601fe52..c45b6798 100644
--- a/swanlab/data/run/config.py
+++ b/swanlab/data/run/config.py
@@ -7,7 +7,7 @@
 @Description:
     SwanLabConfig 配置类
 """
-from typing import Any, Mapping, Union
+from typing import Any, Union
 from collections.abc import MutableMapping
 import yaml
 import argparse
@@ -15,7 +15,7 @@
 import datetime
 import math
 from typing import Callable, Optional
-from .callback import RuntimeInfo
+from swankit.callback import RuntimeInfo
 from swanlab.data.modules import Line
 import re
 import json
@@ -108,9 +108,9 @@ class SwanLabConfig(MutableMapping):
     """
 
     def __init__(
-        self,
-        config: Union[MutableMapping, argparse.Namespace] = None,
-        on_setter: Optional[Callable[[RuntimeInfo], Any]] = None
+            self,
+            config: Union[MutableMapping, argparse.Namespace] = None,
+            on_setter: Optional[Callable[[RuntimeInfo], Any]] = None
     ):
         """
         实例化配置类，如果settings不为None，说明是通过swanlab.init调用的，否则是通过swanlab.config调用的
diff --git a/swanlab/data/run/exp.py b/swanlab/data/run/exp.py
index 961bb7ee..0608c1bd 100644
--- a/swanlab/data/run/exp.py
+++ b/swanlab/data/run/exp.py
@@ -1,10 +1,10 @@
-from swanlab.data.run.settings import SwanDataSettings
 from swanlab.data.modules import DataWrapper, Line
 from swanlab.log import swanlog
 from typing import Dict, Optional
-from swanlab.utils import create_time
-from .callback import MetricInfo, ColumnInfo, RuntimeInfo
+from swankit.env import create_time
+from swankit.callback import MetricInfo, ColumnInfo
 from .operator import SwanLabRunOperator
+from swankit.core import SwanLabSharedSettings
 import json
 import math
 
@@ -15,12 +15,12 @@ class SwanLabExp:
     save keys when running experiments
     """
 
-    def __init__(self, settings: SwanDataSettings, operator: SwanLabRunOperator) -> None:
+    def __init__(self, settings: SwanLabSharedSettings, operator: SwanLabRunOperator) -> None:
         """初始化实验
 
         Parameters
         ----------
-        settings : SwanDataSettings
+        settings : SwanLabSharedSettings
             全局运行时配置
         operator : SwanLabRunOperator
             操作员
diff --git a/swanlab/data/run/main.py b/swanlab/data/run/main.py
index 30103cb2..439204c2 100644
--- a/swanlab/data/run/main.py
+++ b/swanlab/data/run/main.py
@@ -7,19 +7,19 @@
 @Description:
     在此处定义SwanLabRun类并导出
 """
-from .settings import SwanDataSettings
+from swankit.core import SwanLabSharedSettings
 from swanlab.log import swanlog
 from swanlab.data.modules import MediaType, DataWrapper, FloatConvertible, Line
 from .system import get_system_info, get_requirements
 from swanlab.package import get_package_version
-from swanlab.utils.file import check_key_format
 from .config import SwanLabConfig
 from enum import Enum
 from .exp import SwanLabExp
 from datetime import datetime
 from typing import Callable, Optional, Dict
 from .operator import SwanLabRunOperator, RuntimeInfo
-from swanlab.env import get_mode
+from ..formater import check_key_format
+from swanlab.env import get_mode, get_swanlog_dir
 import random
 
 
@@ -90,8 +90,13 @@ def __init__(
         self.__run_id = "run-{}-{}".format(timestamp, _id)
         # 操作员初始化
         self.__operator = SwanLabRunOperator() if operator is None else operator
-        self.__settings = SwanDataSettings(run_id=self.__run_id, should_save=not self.__operator.disabled)
-        self.__operator.inject(self.__settings)
+        self.__settings = SwanLabSharedSettings(
+            logdir=get_swanlog_dir(),
+            run_id=self.__run_id,
+            should_save=not self.__operator.disabled,
+            version=get_package_version(),
+        )
+        self.__operator.before_run(self.__settings)
         # ---------------------------------- 初始化日志记录器 ----------------------------------
         swanlog.level = self.__check_log_level(log_level)
         # ---------------------------------- 初始化配置 ----------------------------------
@@ -215,7 +220,7 @@ def finish(state: SwanLabRunState = SwanLabRunState.SUCCESS, error=None):
         return _run
 
     @property
-    def settings(self) -> SwanDataSettings:
+    def settings(self) -> SwanLabSharedSettings:
         """
         This property allows you to access the 'settings' content passed through `init`,
         and runtime settings can not be modified.
diff --git a/swanlab/data/run/operator.py b/swanlab/data/run/operator.py
index 04d80284..faa1de97 100644
--- a/swanlab/data/run/operator.py
+++ b/swanlab/data/run/operator.py
@@ -8,15 +8,15 @@
     回调函数操作员，批量处理回调函数的调用
 """
 from typing import List, Union, Dict, Any, Callable
-from .callback import SwanLabRunCallback, MetricInfo, ColumnInfo, OperateErrorInfo, RuntimeInfo
-from swanlab.data.run.settings import SwanDataSettings
+from swankit.callback import SwanKitCallback, MetricInfo, ColumnInfo, OperateErrorInfo, RuntimeInfo
+from swankit.core import SwanLabSharedSettings
 import swanlab.error as E
-from swanlab.utils import FONT
+from swankit.log import FONT
 
 OperatorReturnType = Dict[str, Any]
 
 
-class SwanLabRunOperator(SwanLabRunCallback):
+class SwanLabRunOperator(SwanKitCallback):
     """
     The SwanLabRunOperator is used to batch process callback instances, triggering them respectively in the order of
     injection when each occasion occurs. In design, we aim for isolation between each instance. However, if there are
@@ -24,9 +24,9 @@ class SwanLabRunOperator(SwanLabRunCallback):
     as SwanLabRunOperator itself will not address these situations for now.
     """
 
-    def __init__(self, callbacks: Union[SwanLabRunCallback, List[SwanLabRunCallback]] = None):
+    def __init__(self, callbacks: Union[SwanKitCallback, List[SwanKitCallback]] = None):
         super(SwanLabRunOperator, self).__init__()
-        callbacks = [callbacks] if isinstance(callbacks, SwanLabRunCallback) else callbacks
+        callbacks = [callbacks] if isinstance(callbacks, SwanKitCallback) else callbacks
         self.callbacks = {}
         if callbacks is not None:
             for callback in callbacks:
@@ -40,8 +40,8 @@ def disabled(self):
         """
         return len(self.callbacks) == 0
 
-    def add_callback(self, callback: SwanLabRunCallback):
-        if not isinstance(callback, SwanLabRunCallback):
+    def add_callback(self, callback: SwanKitCallback):
+        if not isinstance(callback, SwanKitCallback):
             raise TypeError(f"Unsupported callback type: {type(callback)}")
         if str(callback) == str(self) or callback in self.callbacks:
             raise ValueError(f"Cannot add the same callback instance: {callback}")
@@ -66,21 +66,20 @@ def parse_return(cls, ret: OperatorReturnType, key: str = None):
             return ret[key]
         return next((v for v in ret.values() if v is not None), None)
 
-    def on_init(self, proj_name: str, workspace: str, logdir: str = None) -> OperatorReturnType:
+    def on_init(self, proj_name: str, workspace: str, logdir: str = None, **kwargs) -> OperatorReturnType:
         return self.__run_all("on_init", proj_name, workspace, logdir=logdir)
 
-    def inject(self, settings: SwanDataSettings) -> OperatorReturnType:
-        self.settings = settings
-        return {name: callback.inject(settings) for name, callback in self.callbacks.items()}
+    def before_run(self, settings: SwanLabSharedSettings):
+        return self.__run_all("before_run", settings)
 
     def before_init_experiment(
-        self,
-        run_id: str,
-        exp_name: str,
-        description: str,
-        num: int,
-        suffix: str,
-        setter: Callable[[str, str, str, str], None]
+            self,
+            run_id: str,
+            exp_name: str,
+            description: str,
+            num: int,
+            suffix: str,
+            setter: Callable[[str, str, str, str], None]
     ):
         return self.__run_all(
             "before_init_experiment",
diff --git a/swanlab/data/run/settings.py b/swanlab/data/run/settings.py
deleted file mode 100644
index 91511965..00000000
--- a/swanlab/data/run/settings.py
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024-01-01 15:45:15
-@File: swanlab/database/settings.py
-@IDE: vscode
-@Description:
-    数据收集部分配置，此为运行时生成的配置，
-"""
-import os
-from swanlab.env import get_swanlog_dir
-from typing import Tuple
-from swanlab.package import get_package_version
-
-
-class LazySettings:
-    """
-    需要外界设置的信息，他们并不在一开始就被赋予意义，如果在设置前访问，返回None
-    """
-
-    def __init__(self):
-        self.__exp_name = None
-        self.__exp_colors = None
-        self.__description = None
-
-    @property
-    def exp_name(self) -> str:
-        """实验名称"""
-        if self.__exp_name is None:
-            raise ValueError("exp_name is None before set")
-        return self.__exp_name
-
-    @exp_name.setter
-    def exp_name(self, exp_name: str) -> None:
-        """实验名称"""
-        if self.__exp_name is not None:
-            raise ValueError("exp_name can only be set once")
-        self.__exp_name = exp_name
-
-    @property
-    def exp_colors(self) -> Tuple[str, str]:
-        """实验颜色"""
-        return self.__exp_colors
-
-    @exp_colors.setter
-    def exp_colors(self, exp_colors: Tuple[str, str]) -> None:
-        """实验颜色"""
-        if self.__exp_colors is not None:
-            raise ValueError("exp_colors can only be set once")
-        self.__exp_colors = exp_colors
-
-    @property
-    def description(self) -> str:
-        """实验描述"""
-        return self.__description
-
-    @description.setter
-    def description(self, description: str) -> None:
-        """实验描述"""
-        if self.__description is not None:
-            raise ValueError("description can only be set once")
-        self.__description = description
-
-
-class SwanDataSettings(LazySettings):
-    """
-    SwanLabRun的配置信息，包括当前实验路径信息等
-
-    涉及路径的属性都已经自动转换为绝对路径，并且文件夹路径在`should_save=True`时会自动创建
-
-    "几乎"所有属性都为只读属性，只有在初始化时可以设置
-    """
-
-    def __init__(self, run_id: str, should_save: bool) -> None:
-        """
-        初始化
-        :param run_id: 实验运行id
-        :param should_save: 是否应该保存实验相关信息，如果保存，相关文件夹将自动创建（文件不会自动创建）
-        """
-        LazySettings.__init__(self)
-        # ---------------------------------- 静态信息 ----------------------------------
-        self.__should_save = should_save
-        self.__run_id = run_id
-        self.__version = get_package_version()
-        # ---------------------------------- 文件夹信息 ----------------------------------
-        logdir = get_swanlog_dir()
-        self.__root_dir = os.path.dirname(logdir)
-        self.__swanlog_dir = logdir
-        self.__run_dir = os.path.join(logdir, run_id)
-        self.__console_dir = os.path.join(self.run_dir, "console")
-        self.__log_dir = os.path.join(self.run_dir, "logs")
-        self.__files_dir = os.path.join(self.run_dir, "files")
-        self.__media_dir = os.path.join(self.run_dir, "media")
-        # ---------------------------------- 文件信息 ----------------------------------
-        self.__error_path = os.path.join(self.console_dir, "error.log")
-
-    def mkdir(self, path: str) -> None:
-        """创建目录"""
-        if not os.path.exists(path) and self.should_save:
-            os.makedirs(path, exist_ok=True)
-
-    # ---------------------------------- 静态属性 ----------------------------------
-
-    @property
-    def should_save(self):
-        """
-        是否应该保存实验信息
-        """
-        return self.__should_save
-
-    @property
-    def version(self) -> str:
-        return self.__version
-
-    @property
-    def run_id(self) -> str:
-        """实验运行id"""
-        return self.__run_id
-
-    # ---------------------------------- 文件夹属性 ----------------------------------
-
-    @property
-    def root_dir(self) -> str:
-        """根目录"""
-        # 必然存在，不需要创建
-        return self.__root_dir
-
-    @property
-    def swanlog_dir(self) -> str:
-        """swanlog目录，存储所有实验的日志目录，也是runs.swanlab数据库的存储目录"""
-        self.mkdir(self.__swanlog_dir)
-        return self.__swanlog_dir
-
-    @property
-    def run_dir(self) -> str:
-        """实验日志、信息文件夹路径"""
-        self.mkdir(self.__run_dir)
-        return self.__run_dir
-
-    @property
-    def log_dir(self) -> str:
-        """记录用户日志文件夹路径"""
-        self.mkdir(self.__log_dir)
-        return self.__log_dir
-
-    @property
-    def console_dir(self) -> str:
-        """记录终端日志路径"""
-        self.mkdir(self.__console_dir)
-        return self.__console_dir
-
-    @property
-    def media_dir(self) -> str:
-        """静态资源路径"""
-        self.mkdir(self.__media_dir)
-        return self.__media_dir
-
-    @property
-    def files_dir(self) -> str:
-        """实验配置信息路径"""
-        self.mkdir(self.__files_dir)
-        return self.__files_dir
-
-    # ---------------------------------- 文件属性 ----------------------------------
-
-    @property
-    def error_path(self) -> str:
-        """错误日志文件路径"""
-        return self.__error_path
diff --git a/swanlab/data/run/system/info.py b/swanlab/data/run/system/info.py
index 37677cc7..929684dc 100644
--- a/swanlab/data/run/system/info.py
+++ b/swanlab/data/run/system/info.py
@@ -23,7 +23,7 @@ def __replace_second_colon(input_string, replacement):
     if first_colon_index != -1:
         second_colon_index = input_string.find(":", first_colon_index + 1)
         if second_colon_index != -1:
-            return input_string[:second_colon_index] + replacement + input_string[second_colon_index + 1 :]
+            return input_string[:second_colon_index] + replacement + input_string[second_colon_index + 1:]
     return input_string
 
 
@@ -108,7 +108,7 @@ def __get_nvidia_gpu_info():
                 gpu_name = gpu_name.decode("utf-8")
             info["type"].append(gpu_name)
             # 获取 GPU 的总显存, 单位为GB
-            info["memory"].append(round(pynvml.nvmlDeviceGetMemoryInfo(handle).total / (1024**3)))
+            info["memory"].append(round(pynvml.nvmlDeviceGetMemoryInfo(handle).total / (1024 ** 3)))
 
     except pynvml.NVMLError as e:
         swanlog.debug(f"An error occurred when getting GPU info: {e}")
@@ -120,17 +120,17 @@ def __get_nvidia_gpu_info():
 
 
 def __get_apple_gpu_info():
-    import ujson
+    import json
 
     info = {"cores": None, "type": [], "memory": []}
 
     # 使用system_profiler命令以JSON格式获取GPU信息
     try:
         result = subprocess.run(["system_profiler", "SPHardwareDataType", "-json"], capture_output=True, text=True)
-        gpu_name = ujson.loads(result.stdout)["SPHardwareDataType"][0]["chip_type"]
-        memory = ujson.loads(result.stdout)["SPHardwareDataType"][0]["physical_memory"]
+        gpu_name = json.loads(result.stdout)["SPHardwareDataType"][0]["chip_type"]
+        memory = json.loads(result.stdout)["SPHardwareDataType"][0]["physical_memory"]
         memory = str(memory).lower().replace("gb", "")
-        number_processors = ujson.loads(result.stdout)["SPHardwareDataType"][0]["number_processors"]
+        number_processors = json.loads(result.stdout)["SPHardwareDataType"][0]["number_processors"]
     except:
         return None
 
@@ -147,7 +147,7 @@ def __get_apple_gpu_info():
     # try:
     #     command = [str(binary_path), "--json"]
     #     output = (subprocess.check_output(command, universal_newlines=True).strip().split("\n"))[0]
-    #     raw_stats = ujson.loads(output)
+    #     raw_stats = json.loads(output)
     #     stats = {
     #         "gpu": raw_stats["utilization"],
     #         "memoryAllocated": raw_stats["mem_used"],
@@ -190,7 +190,7 @@ def __get_memory_size():
     try:
         # 获取系统总内存大小
         mem = psutil.virtual_memory()
-        total_memory = round(mem.total / (1024**3))  # 单位为GB
+        total_memory = round(mem.total / (1024 ** 3))  # 单位为GB
         return total_memory
     except Exception as e:
         swanlog.debug(f"An error occurred when getting memory size: {e}")
diff --git a/swanlab/data/sdk.py b/swanlab/data/sdk.py
index c741a3a7..4df26776 100644
--- a/swanlab/data/sdk.py
+++ b/swanlab/data/sdk.py
@@ -16,14 +16,14 @@
     register,
     get_run,
 )
+from .formater import check_load_json_yaml, check_proj_name_format
 from .callback_cloud import CloudRunCallback
 from .callback_local import LocalRunCallback
 from .run.operator import SwanLabRunOperator
-from swanlab.env import init_env, get_swanlog_dir, SwanLabMode, MODE, ROOT
 from swanlab.log import swanlog
-from swanlab.utils import check_load_json_yaml, check_proj_name_format
 from swanlab.api import code_login
-from swanlab.package import version_limit
+from swanlab.env import SwanLabEnv
+from swankit.env import SwanLabMode
 from swanboard import SwanBoardCallback
 
 
@@ -168,9 +168,6 @@ def init(
     )
     # 初始化confi参数
     config = _init_config(config)
-    init_env()
-    # 检查logdir内文件的版本，如果<=0.1.4则报错
-    version_limit(get_swanlog_dir(), mode="init")
     # ---------------------------------- 实例化实验 ----------------------------------
     # 注册实验
     run = register(
@@ -251,14 +248,15 @@ def _init_mode(mode: str = None):
     :raise ValueError: mode参数不合法
     """
     allowed = [m.value for m in SwanLabMode]
-    m = os.environ.get(MODE)
-    if m is not None and mode is not None:
-        swanlog.warning(f"The environment variable {MODE} will be overwritten by the parameter mode")
-    mode = m if mode is None else mode
+    mode_key = SwanLabEnv.SWANLAB_MODE.value
+    mode_value = os.environ.get(mode_key)
+    if mode_value is not None and mode is not None:
+        swanlog.warning(f"The environment variable {mode_key} will be overwritten by the parameter mode")
+    mode = mode_value if mode is None else mode
     if mode is not None and mode not in allowed:
         raise ValueError(f"`mode` must be one of {allowed}, but got {mode}")
     mode = "cloud" if mode is None else mode
-    os.environ[MODE] = mode
+    os.environ[mode_key] = mode
     return mode
 
 
diff --git a/swanlab/env.py b/swanlab/env.py
index ff763cb9..a134c8ad 100644
--- a/swanlab/env.py
+++ b/swanlab/env.py
@@ -7,248 +7,82 @@
     swanlab全局共用环境变量(运行时环境变量)
     除了utils和error模块，其他模块都可以使用这个模块
 """
-import os
-from typing import MutableMapping, Optional
-from .error import UnKnownSystemError
+from typing import List
+import swankit.env as E
+from swankit.env import SwanLabSharedEnv
 import enum
-import sys
 
-Env = Optional[MutableMapping]
 
-_env = dict()
-"""运行时环境变量参数存储，实际上就是一个字典"""
+# ---------------------------------- 环境变量枚举类 ----------------------------------
 
-# ---------------------------------- 基础环境变量 ----------------------------------
-# '描述' = "key"
-ROOT = "SWANLAB_LOG_DIR"
-"""命令执行目录SWANLAB_LOG_DIR，日志文件存放在这个目录下，如果自动生成，则最后的目录名为swanlog，第一次调用时如果路径不存在，会自动创建路径"""
 
-PORT = "SWANLAB_SERVER_PORT"
-"""cli 服务端口"""
-
-HOST = "SWANLAB_SERVER_HOST"
-"""cli 服务地址"""
-
-DEV = "SWANLAB_DEV"
-"""是否是开发模式SWANLAB_DEV，开发模式下会打印更多的日志信息，并且切换一些配置"""
-
-MODE = "SWANLAB_MODE"
-"""运行模式SWANLAB_MODE，有cloud、local、disabled等模式，针对Callback回调的不同实例化方式，具体效果为：
-1. disabled: 禁用模式，不会上传日志到云端，也不会记录日志，swanlab只会执行解析log的功能，不会输出任何内容到本地
-2. cloud: 云端模式，是云端与本地的混合模式，会上传日志到云端，也会记录日志到本地
-3. cloud-only: 云端模式，只会上传日志到云端，不会记录日志到本地，但是会生成一些临时文件
-4. local: 本地模式，不会上传日志到云端，使用swanlab本地版本
-此外，SWANLAB_MODE为disabled时，会开启非严格模式，非严格模式不再要求文件路径存在
-"""
-PACKAGE = "SWANLAB_PACKAGE_PATH"
-"""swanlab包路径SWANLAB_PACKAGE_PATH，用于开发模式下指定swanlab包的路径
-当SWANLAB_DEV为TRUE时，必须指定此路径，否则会抛出异常，非开发模式下此环境变量无效
-"""
-HOME = "SWANLAB_HOME"
-"""存放.swanlab文件夹的路径，用于开发模式下的测试，非开发模式下此环境变量无效
-"""
-
-
-class SwanLabMode(enum.Enum):
-    DISABLED = "disabled"
-    CLOUD = "cloud"
-    # CLOUD_ONLY = "cloud-only"
-    LOCAL = "local"
-
-
-def get_mode(env: Optional[Env] = None) -> Optional[str]:
-    """
-    获取运行模式，返回值为SwanLabMode枚举value
-    """
-    if _env.get(MODE) is not None:
-        return _env.get(MODE)
-    # 否则从环境变量中提取
-    if env is None:
-        env = os.environ
-    default: Optional[str] = "cloud"
-    mode = env.get(MODE, default)
-    allowed = [mode.value for mode in SwanLabMode]
-    if mode not in allowed:
-        raise ValueError('SWANLAB_MODE must be one of {allowed}, now is "{mode}"'.format(allowed=allowed, mode=mode))
-    _env[MODE] = mode
-    return mode
-
-
-def get_swanlog_dir(env: Optional[Env] = None) -> Optional[str]:
-    """获取swanlog路径，返回值为绝对路径
-
-    Returns
-    -------
-    Optional[str]
-        swanlog目录路径
-    """
-    if _env.get(ROOT) is not None:
-        return _env.get(ROOT)
-    # 否则从环境变量中提取
-    if env is None:
-        env = os.environ
-    # 默认为当前目录下的swanlog目录
-    default: Optional[str] = os.path.join(os.getcwd(), "swanlog")
-    path = env.get(ROOT, default=default)
-    # 必须是一个绝对路径
-    if not os.path.isabs(path):
-        raise ValueError('SWANLAB_LOG_DIR must be an absolute path, now is "{path}"'.format(path=path))
-    # 严格模式路径必须存在
-    assert_exist(
-        path,
-        target_type="folder",
-        desc=(
-            'The log file was not found in the default path "{path}". '
-            'Please use the "swanlab watch <LOG '
-            'PATH>" command to specify the location of the log path."'.format(path=path)
-            if path == default
-            else 'SWANLAB_LOG_DIR must be an existing path, now is "{path}"'.format(path=path)
-        ),
-        t_desc='SWANLAB_LOG_DIR must be a directory, now is "{path}"'.format(path=path),
-    )
-    _env[ROOT] = path
-    return path
-
-
-def is_dev(env: Optional[Env] = None) -> bool:
-    """判断是否是开发模式
-    此函数会在一开始就被package.py调用，所以不需要判断是否已经初始化
-
-    Returns
-    -------
-    bool
-        是否是开发模式
-    """
-    if _env.get(DEV) is not None:
-        return _env.get(DEV) == "TRUE"
-    # 否则从环境变量中提取
-    if env is None:
-        env = os.environ
-    _env[DEV] = env.get(DEV, default=False)
-    return _env.get(DEV) == "TRUE"
-
-
-# ---------------------------------- 初始化基础环境变量 ----------------------------------
-
-# 所有的初始化函数
-function_list = [get_mode, get_swanlog_dir]
+class SwanLabEnv(enum.Enum):
+    """
+    swanlab环境变量枚举类，包含swankit的共享环境变量
+    """
+    SWANLAB_FOLDER = SwanLabSharedEnv.SWANLAB_FOLDER.value
+    """
+    swanlab全局文件夹保存的路径，默认为用户主目录下的.swanlab文件夹
+    """
+    SWANLOG_FOLDER = SwanLabSharedEnv.SWANLOG_FOLDER.value
+    """
+    swanlab解析日志文件保存的路径，默认为当前运行目录的swanlog文件夹
+    """
+    SWANLAB_MODE = SwanLabSharedEnv.SWANLAB_MODE.value
+    """
+    swanlab的解析模式，涉及操作员注册的回调，目前有三种：local、cloud、disabled，默认为cloud
+    大小写不敏感
+    """
+    SWANBOARD_PROT = "SWANLAB_BOARD_PORT"
+    """
+    cli swanboard 服务端口
+    """
+    SWANBOARD_HOST = "SWANLAB_BOARD_HOST"
+    """
+    cli swanboard 服务地址
+    """
+    SWANLAB_WEB_HOST = "SWANLAB_WEB_HOST"
+    """
+    swanlab云端环境的web地址
+    """
+    SWANLAB_API_HOST = "SWANLAB_API_HOST"
+    """
+    swanlab云端环境的api地址
+    """
+    SWANLAB_VERSION = "SWANLAB_VERSION"
+    """
+    swanlab的版本号，主要用于开发者调试
+    """
 
+    @classmethod
+    def list(cls) -> List[str]:
+        """
+        获取所有的枚举值
+        :return: 所有的枚举值
+        """
+        return [item.value for item in cls]
 
-def init_env(env: Optional[Env] = None):
-    """初始化环境变量
 
-    Parameters
-    ----------
-    env : Optional[Env], optional
-        环境变量map,可以是任意实现了MutableMapping的对象, 默认将使用os.environ
-    """
-    reset_env()
-    for func in function_list:
-        func(env)
+# ---------------------------------- API ----------------------------------
 
+is_windows = E.is_windows
 
-def reset_env():
-    """重置"""
-    _env.clear()
+get_mode = E.get_mode
 
+get_swanlog_dir = E.get_swanlog_dir
 
-# ---------------------------------- 定义计算变量访问方法 ----------------------------------
+get_save_dir = E.get_save_dir
 
 
-def is_strict_mode() -> bool:
-    """
-    是否是严格模式，严格模式下会要求文件路径存在，否则会抛出异常
+def in_jupyter() -> bool:
     """
-    return get_mode() != SwanLabMode.DISABLED.value
-
-
-def is_windows() -> bool:
-    """判断当前操作系统是否是windows还是类unix系统
-    此外的系统会报错为 UnKnownSystemError
-
-    Returns
-    -------
-    bool
-        是否是windows
+    用于检测是否在 notebook jupyter 中运行
+    :return: bool 是否在 notebook 中运行
     """
-    if sys.platform.startswith("win"):
+    try:
+        # notebook 中会有 __IPYTHON__，而正常环境没有定义，所以 try
+        # 'type: ignore': 可以让 pylance 忽略对变量定义的检查
+        _ = __IPYTHON__  # type: ignore
         return True
-    elif sys.platform.startswith("linux") or sys.platform.startswith("darwin"):
+    except NameError:
         return False
-    raise UnKnownSystemError("Unknown system, not windows or unix-like system")
-
-
-def get_user_home() -> str:
-    """获取用户家目录，需要分为windows和类unix系统
-
-    Returns
-    -------
-    str
-        用户家目录
-    """
-    if is_windows():
-        return os.environ.get("USERPROFILE")
-    else:
-        return os.environ.get("HOME")
-
-
-def get_swanlab_folder() -> str:
-    """获取用户家目录的.swanlab文件夹路径，如果不存在此文件夹就创建
-
-    Returns
-    -------
-    str
-        用户家目录的.swanlab文件夹路径
-    """
-    if is_dev() and HOME in os.environ:
-        path = os.path.join(os.environ[HOME], ".swanlab")
-        if not assert_exist(path, target_type="folder", ra=False):
-            os.mkdir(path)
-        return path
-
-    user_home = get_user_home()
-    swanlab_folder = os.path.join(user_home, ".swanlab")
-    try:
-        if not assert_exist(swanlab_folder, ra=False, target_type="folder"):
-            os.mkdir(swanlab_folder)
-    except NotADirectoryError:
-        os.remove(swanlab_folder)
-        os.mkdir(swanlab_folder)
-    return swanlab_folder
-
-
-def get_package_path() -> Optional[str]:
-    if is_dev() and PACKAGE in os.environ:
-        return os.environ[PACKAGE]
-    else:
-        return os.path.join(os.path.dirname(__file__), "package.json")
-
-
-def assert_exist(path: str, target_type: str = None, ra: bool = True, desc: str = None, t_desc: str = None) -> bool:
-    """
-    检查文件是否存在，严格模式下，文件不存在会抛出异常，或者可以手动通过参数控制，存在则返回True，否则返回False
-    :param path: 文件路径
-    :param target_type: 文件类型(folder, file)，如果文件类型与预期不符，会抛出异常，非严格模式下不检测，为None不检测文件类型
-    :param ra: 文件不存在时是否抛出异常，非严格模式下强制不抛出，此参数无效，此参数只影响文件是否存在，不影响文件类型判断时抛出异常
-    :param desc: 异常描述信息，非严格模式下强制不抛出，此参数无效
-    :param t_desc: 文件类型描述信息，非严格模式下强制不抛出，此参数无效
-    :raises FileNotFoundError: 文件不存在时抛出异常
-    :raises NotADirectoryError: 文件夹是一个文件时抛出异常
-    :raises IsADirectoryError: 文件是一个文件夹时抛出异常
-    """
-    if not is_strict_mode():
-        return os.path.exists(path)
-    if not os.path.exists(path):
-        if ra:
-            raise FileNotFoundError(desc or "{path} not existed".format(path=path))
-        else:
-            return False
-    # 检查文件类型
-    if target_type is not None:
-        if target_type == "folder":
-            if not os.path.isdir(path):
-                raise NotADirectoryError(t_desc or "{path} is not a folder".format(path=path))
-        elif target_type == "file":
-            if not os.path.isfile(path):
-                raise IsADirectoryError(t_desc or "{path} is not a file".format(path=path))
-    return True
diff --git a/swanlab/error.py b/swanlab/error.py
index 3304f59b..1bd018d2 100644
--- a/swanlab/error.py
+++ b/swanlab/error.py
@@ -8,7 +8,6 @@
     swanlab全局错误定义
 """
 import requests
-from typing import Optional
 
 
 class ValidationError(Exception):
@@ -31,12 +30,6 @@ class NotLoginError(Exception):
     pass
 
 
-class UnKnownSystemError(Exception):
-    """未知系统错误，此时swanlab运行在未知系统上，这个系统不是windows或者类unix系统
-    """
-    pass
-
-
 class SyncError(Exception):
     """
     上传错误，作为已知错误捕捉
@@ -48,18 +41,6 @@ def __init__(self, *args):
         self.message = "sync error"
 
 
-class FileError(SyncError):
-    """
-    文件错误，文件不存在或者格式错误，此时可能是系统资源数据由于多线程读写导致的文件错误（为空）
-    在下一次事件循环中会重新读取文件
-    """
-
-    def __init__(self, *args):
-        super().__init__(*args)
-        self.log_level = "debug"
-        self.message = "file read error"
-
-
 class ApiError(SyncError):
     """
     api有关的错误，在聚合器中将捕获他们
@@ -97,4 +78,3 @@ def __init__(self, expected: str, got: str):
         super().__init__(f"expected: {expected}, got: {got}")
         self.expected = expected
         self.got = got
-
diff --git a/swanlab/log/console.py b/swanlab/log/console.py
index 94b572c7..cc0839d8 100644
--- a/swanlab/log/console.py
+++ b/swanlab/log/console.py
@@ -1,8 +1,8 @@
 import sys
 import os
 from datetime import datetime
-from ..utils import FONT
-from swanlab.utils import create_time
+from swankit.log import FONT
+from swankit.env import create_time
 
 
 # 检查当前日期是否和控制台日志文件名一致
diff --git a/swanlab/package.json b/swanlab/package.json
index 4ba0a825..16314fd5 100644
--- a/swanlab/package.json
+++ b/swanlab/package.json
@@ -2,9 +2,5 @@
   "name": "swanlab",
   "version": "0.3.12",
   "description": "",
-  "python": "true",
-  "host": {
-    "web": "https://swanlab.cn",
-    "api": "https://swanlab.cn/api"
-  }
+  "python": "true"
 }
diff --git a/swanlab/package.py b/swanlab/package.py
index 5d2b0842..f015e251 100644
--- a/swanlab/package.py
+++ b/swanlab/package.py
@@ -7,37 +7,38 @@
 @Description:
     用于管理swanlab的包管理器的模块，做一些封装
 """
-import json
-import os
-from .env import get_package_path, get_swanlab_folder, is_strict_mode
-from .utils.key import get_key
+from .env import get_save_dir, SwanLabEnv
 from .error import KeyFileError
 from typing import Optional
 import requests
+import netrc
+import json
+import os
 
-package_path = get_package_path()
-
+package_path = os.path.join(os.path.dirname(__file__), "package.json")
 
-def get_package_version(p=package_path) -> str:
-    """获取swanlab的版本号
 
-    Parameters
-    ----------
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
+# ---------------------------------- 版本号相关 ----------------------------------
 
-    Returns
-    -------
-    str
-        swanlab的版本号
+def get_package_version() -> str:
+    """获取swanlab的版本号
+    :return: swanlab的版本号
     """
+    if SwanLabEnv.SWANLAB_VERSION.value in os.environ:
+        return os.environ[SwanLabEnv.SWANLAB_VERSION.value]
     # 读取package.json文件
-    with open(p, "r") as f:
+    with open(package_path, "r") as f:
         return json.load(f)["version"]
 
 
 def get_package_latest_version(timeout=0.5) -> Optional[str]:
+    """
+    获取swanlab的最新版本号
+    :param timeout: 请求超时时间
+    :return: 最新版本号
+    """
     url = "https://pypi.org/pypi/swanlab/json"
+    # noinspection PyBroadException
     try:
         response = requests.get(url, timeout=timeout)
         if response.status_code == 200:
@@ -45,162 +46,99 @@ def get_package_latest_version(timeout=0.5) -> Optional[str]:
             return data["info"]["version"]
         else:
             return None
-    except Exception as e:
+    except Exception:
         return None
 
 
-def get_host_web(p: str = package_path) -> str:
-    """获取swanlab网站网址
+# ---------------------------------- 云端url相关 ----------------------------------
 
-    Parameters
-    ----------
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
 
-    Returns
-    -------
-    str
-        swanlab网站的网址
+def get_host_web() -> str:
+    """获取swanlab网站网址
+    :return: swanlab网站的网址
     """
-    with open(p, "r", encoding="utf-8") as f:
-        return json.load(f)["host"]["web"]
+    return os.getenv(SwanLabEnv.SWANLAB_WEB_HOST.value, "https://swanlab.cn")
 
 
-def get_host_api(p: str = package_path) -> str:
+def get_host_api() -> str:
     """获取swanlab网站api网址
-
-    Parameters
-    ----------
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
-
-    Returns
-    -------
-    str
-        swanlab网站的api网址
+    :return: swanlab网站的api网址
     """
-    with open(p, "r", encoding="utf-8") as f:
-        return json.load(f)["host"]["api"]
+    return os.getenv(SwanLabEnv.SWANLAB_API_HOST.value, "https://swanlab.cn/api")
 
 
-def get_user_setting_path(p: str = package_path) -> str:
+def get_user_setting_path() -> str:
     """获取用户设置的url
-
-    Parameters
-    ----------
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
-
-    Returns
-    -------
-    str
-        用户设置的url
+    :return: 用户设置的url
     """
-    return get_host_web(p) + "/settings"
+    return get_host_web() + "/settings"
 
 
-def get_project_url(username: str, projname: str, p: str = package_path) -> str:
+def get_project_url(username: str, projname: str) -> str:
     """获取项目的url
-
-    Parameters
-    ----------
-    username : str
-        用户名
-    projname : str
-        项目名
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
-
-    Returns
-    -------
-    str
-        项目的url
+    :param username: 用户名
+    :param projname: 项目名
+    :return: 项目的url
     """
-    return get_host_web(p) + "/" + username + "/" + projname
+    return get_host_web() + "/" + username + "/" + projname
 
 
-def get_experiment_url(username: str, projname: str, expid: str, p: str = package_path) -> str:
+def get_experiment_url(username: str, projname: str, expid: str) -> str:
     """获取实验的url
+    :param username: 用户名
+    :param projname: 项目名
+    :param expid: 实验id
+    :return: 实验的url
+    """
+    return get_project_url(username, projname) + "/" + expid
 
-    Parameters
-    ----------
-    username : str
-        用户名
-    projname : str
-        项目名
-    expid : str
-        实验id
-    p : str, optional
-        package.json文件路径，默认为项目的package.json
-
-    Returns
-    -------
-    str
-        实验的url
+
+# ---------------------------------- 登录相关 ----------------------------------
+
+
+def get_key():
+    """使用标准netrc库解析token文件，获取token
+    :raise KeyFileError: 文件不存在或者host不存在
+    :return: token
     """
-    return get_project_url(username, projname, p) + "/" + expid
-
-
-def version_limit(path: str, mode: str) -> None:
-    """限制版本号在v0.1.5之后
-    主要原因是因为在v0.1.5之后使用了数据库连接，而在之前的版本中并没有使用数据库连接
-    本函数用于检查swanlog文件夹内容是否是v0.1.5之后的版本
-
-    Parameters
-    ----------
-    path : str
-        swanlog目录
-    mode : str
-        模式，只能是['watch', 'init']中的一个，分别对应swanlab watch和swanlab init
-        主要是显示不同的错误信息
-
-    Raises
-    ------
-    ValueError
-        如果版本号低于v0.1.5则抛出异常
+    path = os.path.join(get_save_dir(), ".netrc")
+    host = get_host_api()
+    if not os.path.exists(path):
+        raise KeyFileError("The file does not exist")
+    nrc = netrc.netrc(path)
+    info = nrc.authenticators(host)
+    if info is None:
+        raise KeyFileError(f"The host {host} does not exist")
+    return info[2]
+
+
+def save_key(username: str, password: str, host: str = None):
     """
-    if not is_strict_mode():
-        return
-    # 判断文件夹内是否存在runs.swanlog文件
-    if os.path.exists(os.path.join(path, "runs.swanlog")):
-        return
-    # 不存在则进一步判断，如果存在project.json文件，且可以被json读取，并且存在version字段，则说明版本号小于v0.1.5
-    if os.path.exists(os.path.join(path, "project.json")):
-        with open(os.path.join(path, "project.json"), "r") as f:
-            project = json.load(f)
-            # print(project.get("version"))
-            if project.get("version") is not None:
-                # 报错，当前目录只允许v0.1.5之前的版本，请降级到v0.1.4
-                if mode == "watch":
-                    info = (
-                        "The version of logdir is old (Created by swanlab<=0.1.4), the current version of "
-                        "SwanLab doesn't support this logfile. If you need to watch this logfile, please use the "
-                        "transfer script: https://github.com/SwanHubX/SwanLab/blob/main/script/transfer_logfile_0"
-                        ".1.4.py'"
-                    )
-                elif mode == "init":
-                    info = (
-                        "The version of logdir is old (Created by swanlab<=0.1.4), the current version of "
-                        "SwanLab doesn't support this logfile. If you need to continue train in this logdir, "
-                        "please use the transfer script: "
-                        "https://github.com/SwanHubX/SwanLab/blob/main/script/transfer_logfile_0.1.4.py'"
-                    )
-                else:
-                    info = "version_limit function only support mode in ['watch', 'init']"
-                raise ValueError(info)
-    return
+    保存key到对应的文件目录下，文件名称为.netrc（basename）
+    此函数不考虑上层文件存在的清空，但是会在调用的get_save_dir()函数中进行检查
+    :param username: 保存的用户名
+    :param password: 保存的密码
+    :param host: 保存的host
+    """
+    if host is None:
+        host = get_host_api()
+    path = os.path.join(get_save_dir(), ".netrc")
+    if not os.path.exists(path):
+        with open(path, "w") as f:
+            f.write("")
+    nrc = netrc.netrc(path)
+    nrc.hosts[host] = (username, None, password)
+    with open(path, "w") as f:
+        f.write(nrc.__repr__())
 
 
 def is_login() -> bool:
-    """判断是否已经登录
-
-    Returns
-    -------
-    bool
-        是否已经登录，但是不保证登录的key可用
+    """判断是否已经登录，与当前的host相关
+    但不会检查key的有效性
+    :return: 是否已经登录
     """
     try:
-        _ = get_key(os.path.join(get_swanlab_folder(), ".netrc"), get_host_api())[2]
+        _ = get_key()
         return True
     except KeyFileError:
         return False
diff --git a/swanlab/utils/__init__.py b/swanlab/utils/__init__.py
deleted file mode 100644
index 55750c3d..00000000
--- a/swanlab/utils/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-"""
-工具函数，用于提供一些常用的工具函数，与项目本身无关，在项目任何文件中都可以使用
-"""
-from .font import generate_color, FONT, COLOR_LIST
-from .time import create_time
-from .file import check_load_json_yaml, check_proj_name_format, check_exp_name_format
diff --git a/swanlab/utils/font.py b/swanlab/utils/font.py
deleted file mode 100644
index 753246e3..00000000
--- a/swanlab/utils/font.py
+++ /dev/null
@@ -1,353 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2023-12-11 21:41:08
-@File: swanlab/utils/color.py
-@IDE: vscode
-@Description:
-    颜色处理工具
-"""
-
-import sys
-import re
-from typing import Callable, Tuple
-import threading
-import time
-
-light_colors = [
-    "#528d59",  # 绿色
-    "#587ad2",  # 蓝色
-    "#c24d46",  # 红色
-    "#9cbe5d",  # 青绿色
-    "#6ebad3",  # 天蓝色
-    "#dfb142",  # 橙色
-    "#6d4ba4",  # 紫色
-    "#8cc5b7",  # 淡青绿色
-    "#892d58",  # 紫红色
-    "#40877c",  # 深青绿色
-    "#d0703c",  # 深橙色
-    "#d47694",  # 粉红色
-    "#e3b292",  # 淡橙色
-    "#b15fbb",  # 浅紫红色
-    "#905f4a",  # 棕色
-    "#989fa3",  # 灰色
-]
-
-COLOR_LIST = {
-    "light": light_colors,
-    "dark": light_colors,
-}
-
-
-def generate_color(number: int = 1):
-    """输入数字，在设定好顺序的颜色列表中返回十六进制颜色字符串
-
-    Returns
-    -------
-    str
-        颜色字符串,以#开头的十六进制字符串,如#FFFFFF
-        字符串字母大写
-    """
-
-    if number % 16 == 0:
-        number = 16
-    else:
-        number = number % 16
-
-    return COLOR_LIST["light"][number - 1], COLOR_LIST["dark"][number - 1]
-
-
-class FONT:
-
-    @staticmethod
-    def loading(
-        s: str,
-        func: Callable,
-        args: Tuple = (),
-        interval: float = 0.4,
-        prefix: str = None,
-        brush_length: int = 100
-    ):
-        """
-        实现终端打印的加载效果，输入的字符串会在开头出现loading效果以等待传入的函数执行完毕
-
-        Parameters
-        ----------
-        s : str
-            需要打印的字符串
-        func : coroutine
-            执行的同步函数
-        args : Tuple, optional
-            传入函数的参数，默认为空
-        interval : float, optional
-            loading的速度，即每个字符的间隔时间，单位为秒
-        prefix : str, optional
-            前缀字符串，打印在loading效果之前，默认为swanlab
-        brush_length : int, optional
-            刷去的长度，默认为100
-        """
-        # FIXME 因为协程有一些适配性问题，暂时使用线程
-        prefix = FONT.bold(FONT.blue("swanlab")) + ": " if prefix is None else prefix
-        symbols = ["\\", "|", "/", "-"]
-
-        running, result, error = True, None, None
-
-        def loading():
-            index = 0
-            while True:
-                sys.stdout.write("\r" + prefix + symbols[index % len(symbols)] + " " + s)
-                sys.stdout.flush()
-                index += 1
-                time.sleep(interval)
-                if not running:
-                    break
-
-        # 再次封装传入的func，当func执行完毕以后，将running置为False
-        def task():
-            nonlocal result, error, running
-            try:
-                result = func(*args)
-            except Exception as e:
-                error = e
-            finally:
-                running = False
-
-        # 开启新线程
-        t1 = threading.Thread(target=loading)
-        t2 = threading.Thread(target=task)
-        t1.start()
-        t2.start()
-        try:
-            t2.join()
-        except KeyboardInterrupt:
-            running = False
-            raise KeyboardInterrupt
-        t1.join()
-        if error is not None:
-            raise error
-        FONT.brush("", brush_length)
-        return result
-
-    @staticmethod
-    def swanlab(s: str, color: str = "blue"):
-        """用于为某一条信息添加swanlab前缀"""
-        return FONT.bold(getattr(FONT, color)("swanlab")) + ": " + s
-
-    @staticmethod
-    def brush(s: str, length: int = 20) -> None:
-        """
-        将当前终端行刷去，替换为新的字符串
-
-        Parameters
-        ----------
-        s : str
-            需要刷去的字符串
-        length : int, optional
-            需要刷去的长度，默认为20，如果当前行的长度大于length，但又需要刷去整行，则需要传入更大的length
-        """
-        sys.stdout.write("\r" + " " * length + "\r" + s)
-        sys.stdout.flush()
-
-    @staticmethod
-    def bold(s: str) -> str:
-        """在终端中加粗字符串
-
-        Parameters
-        ----------
-        s : str
-            需要加粗的字符串
-
-        Returns
-        -------
-        str
-            加粗后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[1m{s}\033[0m"
-
-    @staticmethod
-    def default(s: str) -> str:
-        """在终端中将字符串着色为默认颜色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[39m{s}\033[0m"
-
-    @staticmethod
-    def blue(s: str) -> str:
-        """在终端中将字符串着色为蓝色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[34m{s}\033[0m"
-
-    @staticmethod
-    def grey(s: str) -> str:
-        """在终端中将字符串着色为灰色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[90m{s}\033[0m"
-
-    @staticmethod
-    def underline(s: str) -> str:
-        """在终端中将字符串着色为下划线
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[4m{s}\033[0m"
-
-    @staticmethod
-    def green(s: str) -> str:
-        """在终端中将字符串着色为绿色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[32m{s}\033[0m"
-
-    @staticmethod
-    def dark_green(s: str) -> str:
-        """在终端中将字符串着色为深绿色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[38;5;22m{s}\033[0m"
-
-    @staticmethod
-    def dark_gray(s: str) -> str:
-        """在终端中将字符串着色为深灰色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[38;5;236m{s}\033[0m"
-
-    @staticmethod
-    def yellow(s: str) -> str:
-        """在终端中将字符串着色为黄色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[33m{s}\033[0m"
-
-    @staticmethod
-    def red(s: str) -> str:
-        """在终端中将字符串着色为红色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[31m{s}\033[0m"
-
-    @staticmethod
-    def magenta(s: str) -> str:
-        """在终端中将字符串着色为品红色
-
-        Parameters
-        ----------
-        s : str
-            需要着色的字符串
-
-        Returns
-        -------
-        str
-            着色后的字符串
-        """
-        # ANSI 转义码用于在终端中改变文本样式
-        return f"\033[35m{s}\033[0m"
-
-    @staticmethod
-    def clear(s: str) -> str:
-        """清除字符串中的颜色编码
-
-        Parameters
-        ----------
-        s : str
-            需要清除颜色的字符串
-
-        Returns
-        -------
-        str
-            清除颜色后的字符串
-        """
-        ansi_escape_pattern = re.compile(r"\033\[[0-9;]+m")
-        return ansi_escape_pattern.sub("", s)
diff --git a/swanlab/utils/judgment.py b/swanlab/utils/judgment.py
deleted file mode 100644
index 271d7892..00000000
--- a/swanlab/utils/judgment.py
+++ /dev/null
@@ -1,111 +0,0 @@
-def in_jupyter() -> bool:
-    """
-    用于检测是否在 notebook jupyter 中运行
-    :return: bool 是否在 notebook 中运行
-    """
-    try:
-        # notebook 中会有 __IPYTHON__，而正常环境没有定义，所以 try
-        # 'type: ignore': 可以让 pylance 忽略对变量定义的检查
-        _ = __IPYTHON__  # type: ignore
-        return True
-    except NameError:
-        return False
-
-
-def show_button_ipy(experiment_url):
-    try:
-        # noinspection PyPackageRequirements
-        import ipywidgets as widgets
-        # noinspection PyPackageRequirements
-        from IPython.display import display, IFrame
-
-        output = widgets.Output()
-
-        def show_iframe():
-            with output:
-                output.clear_output()
-                # 在这里定义Iframe的内容，例如一个网站的URL
-                iframe = IFrame(experiment_url, width="100%", height=500)
-                display(iframe)
-
-        button = widgets.Button(description="Display SwanLab Dashboard")
-        button.on_click(show_iframe)
-        display(button, output)
-    except ImportError:
-        pass
-
-
-def show_button_html(experiment_url):
-    try:
-        # noinspection PyPackageRequirements
-        from IPython.display import HTML, display
-
-        iframe_h5 = f'<iframe src="{experiment_url}" width=100% height="600" frameborder="no"></iframe>'
-        js_code = f"""
-        <script>
-            function showIframe() {{
-                var iframeHtml = '{iframe_h5}';
-                document.getElementById('iframeContainer').innerHTML = iframeHtml;
-            }}
-        </script>
-        """
-
-        total_h5 = f"""
-<!DOCTYPE html>
-<html lang="en">
-<head>
-    <meta charset="UTF-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <title>Show Iframe</title>
-    {js_code}
-</head>
-<body>
-    <style>
-        .interactive-button {{
-            display: flex;
-            align-items: center;
-            height: 36px;
-            border: 0px;
-            background-color: #2c8f63;
-            color: white;
-            padding: 10px 20px;
-            transition: background-color 0.3s, transform 0.2s;
-        }}
-
-        .interactive-button:hover {{
-            background-color: #5cab87;
-            cursor: pointer;
-        }}
-
-        .interactive-button:active {{
-            background-color: #217952;
-            transform: scale(0.96);
-        }}
-    </style>
-    <br>
-    <button onclick="showIframe()" class="interactive-button">
-        <svg style="height: 16px; margin-right: 8px;" viewBox="0 0 46 46" fill="none">
-            <path
-                d="M10.8439 21.1974C10.6414 21.2854 10.4477 21.3925 10.2655 21.5173L10.2069 21.5652C10.1839 21.58 10.1625 21.5969 10.1429 21.6159C6.29135 24.6118 4.22831 29.4416 5.32646 34.282C5.94656 37.0577 7.50461 39.5348 9.73801 41.2958C11.9714 43.0568 14.7436 43.994 17.5874 43.9495H18.0219C19.8864 43.8697 21.7087 43.3694 23.3526 42.486C24.9964 41.6026 26.4193 40.3589 27.5147 38.848C28.61 37.3371 29.3496 35.598 29.678 33.761C30.0065 31.9239 29.9153 30.0363 29.4112 28.2395C28.9181 26.4723 27.8919 24.8437 26.9937 23.2551C25.4158 20.4653 23.8343 17.6764 22.2492 14.8884C21.7801 14.0647 21.3057 13.2465 20.8419 12.4228C20.2315 11.3353 19.2746 10.1519 19.224 8.86183C19.1733 7.57176 20.2235 6.32701 21.5082 6.07912C23.9284 5.61801 25.0639 8.24078 25.0693 8.23812C25.363 8.94035 25.9123 9.50489 26.6063 9.81764C27.3002 10.1304 28.087 10.168 28.8077 9.92298C29.5283 9.67791 30.1291 9.1684 30.4885 8.49743C30.8479 7.82646 30.9392 7.04405 30.7439 6.30835C30.1514 4.37314 28.9133 2.69953 27.2363 1.56656C25.7615 0.511704 23.9847 -0.0372109 22.1719 0.00195984C20.9049 0.00893199 19.6532 0.27989 18.4967 0.797557C17.3402 1.31522 16.3043 2.06823 15.4551 3.00856C14.49 4.08707 13.7984 5.38193 13.4389 6.78385C13.0794 8.18576 13.0624 9.6536 13.3894 11.0635C13.52 11.593 13.6984 12.1095 13.9225 12.6067C14.5595 14.0514 15.4951 15.3681 16.284 16.7355C17.2525 18.4147 18.2209 20.0948 19.1893 21.7758C20.1578 23.4568 21.1351 25.1449 22.1213 26.8401C22.9209 28.2421 23.7925 29.4682 23.8805 31.1528C23.9175 32.0513 23.7682 32.9479 23.4419 33.7859C23.1156 34.6239 22.6194 35.3854 21.9845 36.0223C21.3496 36.6592 20.5897 37.1578 19.7527 37.4868C18.9157 37.8157 18.0196 37.9678 17.121 37.9336C14.0024 37.7923 11.6488 35.4814 11.1744 32.4588C10.58 28.6419 13.552 26.5469 13.552 26.5469C14.1782 26.1785 14.6497 25.5955 14.8791 24.906C15.1084 24.2166 15.0801 23.4673 14.7993 22.7971C14.5186 22.127 14.0044 21.5813 13.3521 21.2611C12.6998 20.941 11.9536 20.8682 11.2517 21.0561C11.1174 21.0939 10.9856 21.1402 10.8572 21.1947"
-                fill="white"
-            />
-            <path
-                d="M42.8101 31.5968C42.8109 30.5198 42.7218 29.4445 42.5435 28.3823C42.2663 26.7069 41.7464 25.0808 41.0002 23.5552C40.5524 22.6463 39.9874 21.7374 39.1024 21.2417C38.6593 20.9919 38.1589 20.8617 37.6502 20.8639C37.1416 20.8661 36.6423 21.0006 36.2013 21.2541C35.7604 21.5077 35.393 21.8716 35.1352 22.3101C34.8775 22.7485 34.7382 23.2466 34.7312 23.7552C34.7072 24.8773 35.3149 25.8875 35.768 26.9217C36.5212 28.6453 36.8623 30.5208 36.7642 32.3993C36.6661 34.2777 36.1315 36.1075 35.2029 37.7433C35.146 37.8404 35.0952 37.941 35.051 38.0445C34.8623 38.4842 34.7635 38.9573 34.7605 39.4358C34.7802 40.1222 35.0356 40.7808 35.4835 41.3011C35.9315 41.8214 36.5449 42.1717 37.2207 42.2932C38.8759 42.589 40.1899 41.347 40.8856 39.9609C42.1643 37.3589 42.823 34.4961 42.8101 31.5968Z"
-                fill="white"
-            />
-            <path
-                d="M28.2309 11.8938C28.1761 11.9043 28.1218 11.9176 28.0683 11.9338C27.9593 11.9642 27.8611 12.0249 27.7851 12.1088C27.7091 12.1928 27.6584 12.2965 27.6389 12.408C27.6193 12.5195 27.6318 12.6343 27.6748 12.7391C27.7178 12.8438 27.7895 12.9343 27.8818 12.9999C29.2375 14.0252 30.3809 15.3043 31.2482 16.7662C31.4838 17.1677 31.6888 17.5865 31.8612 18.0189C32.0052 18.3921 32.1971 18.8799 32.6822 18.8532C33.0607 18.8346 33.2153 18.512 33.3192 18.1895C33.8137 16.5125 33.9678 14.7534 33.7723 13.0159C33.6331 12.0693 33.4155 11.1359 33.122 10.2252C33.0775 10.0047 32.9744 9.80029 32.8235 9.6335C32.7273 9.54627 32.6054 9.49262 32.4761 9.4806C32.3468 9.46859 32.2171 9.49886 32.1065 9.56687C32.0016 9.65188 31.9115 9.75365 31.8399 9.86806C31.3956 10.4658 30.825 10.9581 30.1687 11.3101C29.8377 11.4861 29.4893 11.6272 29.1292 11.7312C28.828 11.8192 28.5215 11.8325 28.2309 11.8938Z"
-                fill="white"
-            />
-        </svg>
-        Display SwanLab Board
-    </button>
-    <br>
-    <div id="iframeContainer"></div>
-</body>
-</html>
-"""
-
-        display(HTML(total_h5))
-    except ImportError:
-        pass
diff --git a/swanlab/utils/key.py b/swanlab/utils/key.py
deleted file mode 100644
index cbeed2a3..00000000
--- a/swanlab/utils/key.py
+++ /dev/null
@@ -1,66 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024-03-19 19:35:12
-@File: swanlab/utils/key.py
-@IDE: vscode
-@Description:
-    token文件操作，不作为模块方法暴露，如果需要使用相关方法需要指定此文件
-"""
-from ..error import KeyFileError
-import netrc
-import os
-
-
-def get_key(path: str, host: str):
-    """使用标准netrc库解析token文件，获取token
-
-    Parameters
-    ----------
-    path : str
-        token文件路径
-    host : str
-        token对应的host
-
-    Returns
-    -------
-    tuple
-        返回一个元组，如果要获取key，应该使用[2]索引
-
-    Raises
-    ------
-    KeyFileError
-        传入的path路径文件不存在
-    """
-    if not os.path.exists(path):
-        raise KeyFileError("The file does not exist")
-    nrc = netrc.netrc(path)
-    info = nrc.authenticators(host)
-    if info is None:
-        raise KeyFileError(f"The host {host} does not exist")
-    return info
-
-
-def save_key(path: str, host: str, username: str, password: str):
-    """
-    保存key到对应的文件目录下，文件名称为.netrc（basename）
-    :param path: 保存位置，必须是文件夹
-    :param host: 保存的host
-    :param username: 保存的用户名
-    :param password: 保存的密码
-    :raises KeyFileError 传入的path路径文件名称不是.netrc或上级文件夹不存在
-    """
-    # 传入的path路径文件名称不是.netrc
-    if os.path.basename(path) != ".netrc":
-        raise KeyFileError("The file name must be .netrc")
-    # 上级文件夹不存在
-    if not os.path.exists(os.path.dirname(path)):
-        raise KeyFileError("The parent folder does not exist")
-    # 如果文件不存在，自动创建
-    if not os.path.exists(path):
-        with open(path, "w") as f:
-            f.write("")
-    nrc = netrc.netrc(path)
-    nrc.hosts[host] = (username, None, password)
-    with open(path, "w") as f:
-        f.write(nrc.__repr__())
diff --git a/swanlab/utils/time.py b/swanlab/utils/time.py
deleted file mode 100644
index a1d63031..00000000
--- a/swanlab/utils/time.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from datetime import datetime
-
-
-def create_time():
-    """获取当前时间"""
-    return datetime.utcnow().isoformat() + "Z"
-
diff --git a/tutils/__init__.py b/tutils/__init__.py
index 987cf8f9..a1f50c2a 100644
--- a/tutils/__init__.py
+++ b/tutils/__init__.py
@@ -7,39 +7,30 @@
 @Description:
     tutils模块的初始化文件
 """
-
+from swanlab.env import SwanLabEnv
+from .check import *
 from .config import *
-from .utils import *
-import shutil
-import os
 
+api = os.getenv("SWANLAB_API_HOST")
+web = os.getenv("SWANLAB_WEB_HOST")
 
-def clear():
-    """
-    清空临时文件夹, 重新创建
-    """
-    if os.path.exists(TEMP_PATH):
-        shutil.rmtree(TEMP_PATH)
-    os.mkdir(TEMP_PATH)
-    os.mkdir(SWANLAB_LOG_DIR)
-    os.mkdir(SWANLAB_DIR)
 
+def reset_some_env():
+    os.environ[SwanLabEnv.SWANLAB_VERSION.value] = "development"
+    os.environ[SwanLabEnv.SWANLOG_FOLDER.value] = SWANLOG_FOLDER
+    os.environ[SwanLabEnv.SWANLAB_FOLDER.value] = SWANLAB_FOLDER
+    if not TEST_CLOUD_SKIP:
+        os.environ[SwanLabEnv.SWANLAB_API_HOST.value] = api
+        os.environ[SwanLabEnv.SWANLAB_WEB_HOST.value] = web
 
-def init_db():
-    """
-    初始化数据库
-    """
-    from swanboard.db import connect, Project
-    clear()
-    connect(autocreate=True, path=SWANLAB_LOG_DIR)
-    Project.init(name="pytest-swanlab", description="测试swanlab")
+
+reset_some_env()
 
 
 def open_dev_mode() -> str:
     """
     开启开发模式，此时会返回开发环境的api-key并且创建测试目录
+    在上层config部分已经执行了环境变量注入
     :return: api-key
     """
-    # 创建测试目录
-    os.makedirs(SWANLAB_LOG_DIR, exist_ok=True)
-    return CONFIG['api-key']
+    return TEST_CLOUD_KEY
diff --git a/tutils/check.py b/tutils/check.py
new file mode 100644
index 00000000..a60cc305
--- /dev/null
+++ b/tutils/check.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+r"""
+@DATE: 2024/6/22 19:18
+@File: check.py.py
+@IDE: pycharm
+@Description:
+    开发/测试运行前检查，并加载.env文件设置的环境变量
+    此文件应该在顶部导入
+"""
+import os
+import subprocess
+import sys
+from dotenv import load_dotenv
+
+swanlab_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+
+# ---------------------------------- 检查swanboard、swankit包的版本号与当前系统是否一致 ----------------------------------
+
+swanboard = subprocess.run("pip show swanboard", shell=True, capture_output=True).stdout.decode()
+swankit = subprocess.run("pip show swankit", shell=True, capture_output=True).stdout.decode()
+swanboard_version = [i.split(": ")[1] for i in swanboard.split("\n") if i.startswith("Version")][0]
+swankit_version = [i.split(": ")[1] for i in swankit.split("\n") if i.startswith("Version")][0]
+with open(os.path.join(swanlab_dir, "requirements.txt"), "r") as f:
+    packages = f.read().split("\n")
+packages = [i for i in packages if "swanboard" in i or "swankit" in i]
+for i in packages:
+    if "swanboard" in i and swanboard_version not in i:
+        print(f'swanboard过时，运行 pip install -r requirements.txt 进行更新.', file=sys.stderr)
+        sys.exit(2)
+    if "swankit" in i and swankit_version not in i:
+        print(f'swankit过时，运行 pip install -r requirements.txt 进行更新.', file=sys.stderr)
+        sys.exit(2)
+
+# ---------------------------------- 检查是否跳过云测试，如果没跳过，相关环境变量需要指定----------------------------------
+load_dotenv(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), ".env"))
+
+is_pytest_env = 'PYTEST_VERSION' in os.environ
+is_skip_test = os.getenv("TEST_CLOUD_SKIP") is not None
+is_cloud_dev_env = os.getenv("SWANLAB_API_HOST") is not None and os.getenv("SWANLAB_WEB_HOST") is not None
+if not is_cloud_dev_env:
+    # 测试环境
+    if is_pytest_env and not is_skip_test:
+        print("请设置开发云服务环境变量，或者设置环境变量TEST_CLOUD_SKIP以跳过云测试", file=sys.stderr)
+        sys.exit(2)
+    # 开发环境
+    elif not is_pytest_env:
+        print("请设置开发云服务环境变量以运行开发测试脚本", file=sys.stderr)
+        sys.exit(2)
diff --git a/tutils/config.py b/tutils/config.py
index 7f0f4401..9be3c35b 100644
--- a/tutils/config.py
+++ b/tutils/config.py
@@ -8,9 +8,10 @@
     存储一些与单元测试有关的快捷配置
 """
 import os
-import json
 import nanoid
 
+# ---------------------------------- 路径配置 ----------------------------------
+
 __test_path = os.path.join(
     os.path.dirname(
         os.path.dirname(
@@ -21,32 +22,38 @@
 )
 
 TEMP_PATH = os.path.join(__test_path, "temp")
-
-SWANLAB_DIR = os.path.join(TEMP_PATH, ".swanlab")
 """
-测试时.swanlab文件夹存放的位置
+临时文件夹，每一个测试函数在执行前都会清空这个文件夹
 """
 
-SWANLAB_LOG_DIR = os.path.join(TEMP_PATH, "swanlog")
+SWANLOG_FOLDER = os.path.join(TEMP_PATH, "swanlog")
 """
-测试时swanlog文件夹存放的位置
+默认情况下，swanlog保存的文件夹
 """
 
-CONFIG: dict = json.load(open(os.path.join(os.path.dirname(os.path.abspath(__file__)), "config.json")))
+SWANLAB_FOLDER = os.path.join(TEMP_PATH, ".swanlab")
 """
-开发快捷配置
+默认情况下，系统信息保存的文件夹
 """
-KEY: str = CONFIG["api-key"]
+
+# ---------------------------------- 测试用变量 ----------------------------------
+
+TEST_CLOUD_SKIP = os.getenv("TEST_CLOUD_SKIP") is not None
 """
-测试时使用的api-key
+是否跳过云测试
 """
 
-PACKAGE_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), "package.mock.json")
-
-# 注入环境变量
-os.environ["SWANLAB_DEV"] = "TRUE"
-os.environ["SWANLAB_PACKAGE_PATH"] = PACKAGE_PATH
-os.environ["SWANLAB_LOG_DIR"] = SWANLAB_LOG_DIR
-os.environ["SWANLAB_HOME"] = TEMP_PATH
+TEST_CLOUD_KEY = os.getenv("TEST_CLOUD_KEY")
+"""
+云测试的key
+"""
 
-__all__ = ["TEMP_PATH", "SWANLAB_LOG_DIR", "CONFIG", "nanoid", "PACKAGE_PATH", "SWANLAB_DIR", "KEY"]
+# ---------------------------------- 导出 ----------------------------------
+__all__ = [
+    "TEMP_PATH",
+    "nanoid",
+    "TEST_CLOUD_SKIP",
+    "SWANLOG_FOLDER",
+    "SWANLAB_FOLDER",
+    "TEST_CLOUD_KEY",
+]
diff --git a/tutils/utils.py b/tutils/utils.py
deleted file mode 100644
index 2c6fc5b1..00000000
--- a/tutils/utils.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/5/28 16:33
-@File: utils.py
-@IDE: pycharm
-@Description:
-    一些工具函数
-"""
-from nanoid import generate
-from .config import KEY
-
-
-def get_password(prompt: str):
-    # 如果是第一次登录，使用错误的key，会提示重新输入
-    if "Paste" in prompt:
-        return generate()
-    else:
-        return KEY


Test patch:
diff --git a/test/create_error_chart.py b/test/create_error_chart.py
deleted file mode 100644
index 211c8eb0..00000000
--- a/test/create_error_chart.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/5/16 20:42
-@File: create_error_chart.py
-@IDE: pycharm
-@Description:
-    创建错误图表
-"""
-from tutils import open_dev_mode
-import swanlab
-
-swanlab.login(api_key=open_dev_mode())
-
-swanlab.init(
-    description="此实验的图表将出现问题",
-    log_level="debug",
-    mode="cloud",
-)
-
-for i in range(10):
-    swanlab.log({"success": i, "error": "abc"})
-
-for i in range(10):
-    if i % 2 == 1:
-        swanlab.log({"nan_test": "nan", "inf_test": "inf"})
-    else:
-        swanlab.log({"nan_test": i, "inf_test": i})
diff --git a/test/create_experiment.py b/test/create_experiment.py
index 1921ff3f..0cf33b03 100644
--- a/test/create_experiment.py
+++ b/test/create_experiment.py
@@ -30,14 +30,14 @@
     load="test/config/load.yaml",
     mode="cloud",
 )
-swanlab.config.epoches = epochs
+swanlab.config.epochs = epochs
 swanlab.config.learning_rate = lr
 swanlab.config.debug = "这是一串" + "很长" * 100 + "的字符串"
 # 模拟训练
-for epoch in range(2, swanlab.config.epoches):
-    acc = 1 - 2 ** -epoch - random.random() / epoch - offset
-    loss = 2 ** -epoch + random.random() / epoch + offset
-    loss2 = 3 ** -epoch + random.random() / epoch + offset * 3
+for epoch in range(2, swanlab.config.epochs):
+    acc = 1 - 2**-epoch - random.random() / epoch - offset
+    loss = 2**-epoch + random.random() / epoch + offset
+    loss2 = 3**-epoch + random.random() / epoch + offset * 3
     print(f"epoch={epoch}, accuracy={acc}, loss={loss}")
     if epoch % 10 == 0:
         # 测试audio
diff --git a/test/start_server.py b/test/start_server.py
deleted file mode 100644
index a2c37867..00000000
--- a/test/start_server.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2023-11-24 21:12:52
-@File: test/start_server.py
-@IDE: vscode
-@Description:
-    开启后端测试服务，访问端口
-    事实上在使用中并不是这样的，而是在命令行执行命令完成的，这里只是为了测试和开发
-    增加了实际开发中不会用到的热启动功能
-"""
-import uvicorn
-from tutils.config import SWANLAB_LOG_DIR
-from swanboard.db import connect
-from swanboard.app import app
-import os
-
-os.environ["SWANLAB_LOG_DIR"] = SWANLAB_LOG_DIR
-
-connect()
-
-
-def main():
-    return app
-
-
-if __name__ == "__main__":
-    uvicorn.run("start_server:main", host="0.0.0.0", port=6092, reload=True, log_level="critical")
diff --git a/test/unit/api/auth/pytest_login.py b/test/unit/api/auth/pytest_login.py
index 09b1b723..d4836def 100644
--- a/test/unit/api/auth/pytest_login.py
+++ b/test/unit/api/auth/pytest_login.py
@@ -10,34 +10,31 @@
 from swanlab.api.auth.login import login_by_key, terminal_login, code_login
 from swanlab.error import ValidationError
 from swanlab.package import is_login
+from nanoid import generate
 import tutils as T
-from swanlab.env import reset_env, HOME
 import pytest
-import os
 
 
-@pytest.fixture(scope="function", autouse=True)
-def setup_function():
-    """
-    在当前测试文件下的每个测试函数执行前后执行
-    """
-    reset_env()
-    yield
-    reset_env()
-    if HOME in os.environ:
-        del os.environ[HOME]
+def get_password(prompt: str):
+    # 如果是第一次登录，使用错误的key，会提示重新输入
+    if "Paste" in prompt:
+        return generate()
+    else:
+        return T.TEST_CLOUD_KEY
 
 
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_login_success():
     """
     测试登录成功
     """
-    login_info = login_by_key(T.KEY, save=False)
+    login_info = login_by_key(T.TEST_CLOUD_KEY, save=False)
     assert not login_info.is_fail
-    assert login_info.api_key == T.KEY
+    assert login_info.api_key == T.TEST_CLOUD_KEY
     assert login_info.__str__() == "Login success"
 
 
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_login_error_key():
     """
     测试登录失败, 错误的key
@@ -48,26 +45,27 @@ def test_login_error_key():
     assert login_info.__str__() == "Error api key"
 
 
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_terminal_login(monkeypatch):
     """
     测试终端登录
     """
-    os.environ[HOME] = T.TEMP_PATH
-    monkeypatch.setattr("getpass.getpass", T.get_password)
-    login_info = terminal_login(T.KEY)
+    monkeypatch.setattr("getpass.getpass", get_password)
+    login_info = terminal_login(T.TEST_CLOUD_KEY)
     assert not login_info.is_fail
-    assert login_info.api_key == T.KEY
+    assert login_info.api_key == T.TEST_CLOUD_KEY
     assert login_info.__str__() == "Login success"
     assert is_login()
 
 
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_code_login():
     """
     测试code登录
     """
-    login_info = code_login(T.KEY)
+    login_info = code_login(T.TEST_CLOUD_KEY)
     assert not login_info.is_fail
-    assert login_info.api_key == T.KEY
+    assert login_info.api_key == T.TEST_CLOUD_KEY
     assert login_info.__str__() == "Login success"
     with pytest.raises(ValidationError):
         _ = code_login("wrong-key")
diff --git a/test/unit/api/pytest_http.py b/test/unit/api/pytest_http.py
index 631a66a2..281c88a8 100644
--- a/test/unit/api/pytest_http.py
+++ b/test/unit/api/pytest_http.py
@@ -14,11 +14,13 @@
 from swanlab.api.http import create_http, HTTP, CosClient
 from swanlab.api.auth.login import login_by_key
 from swanlab.data.modules import MediaBuffer
-from tutils import KEY, TEMP_PATH
+from tutils import TEST_CLOUD_KEY, TEMP_PATH, TEST_CLOUD_SKIP
+import pytest
 
 alphabet = "abcdefghijklmnopqrstuvwxyz"
 
 
+@pytest.mark.skipif(TEST_CLOUD_SKIP, reason="skip cloud test")
 class TestCosSuite:
     http: HTTP = None
     project_name = nanoid.generate(alphabet)
@@ -31,7 +33,7 @@ class TestCosSuite:
     def setup_class(cls):
         CosClient.REFRESH_TIME = cls.now_refresh_time
         # 这里不测试保存token的功能
-        login_info = login_by_key(KEY, save=False)
+        login_info = login_by_key(TEST_CLOUD_KEY, save=False)
         cls.http = create_http(login_info)
         cls.http.mount_project(cls.project_name)
         cls.http.mount_exp(cls.experiment_name, ('#ffffff', '#ffffff'))
diff --git a/test/unit/api/upload/test_model.py b/test/unit/api/upload/test_model.py
index 35fcedbe..609ba4e7 100644
--- a/test/unit/api/upload/test_model.py
+++ b/test/unit/api/upload/test_model.py
@@ -22,7 +22,6 @@ def test_create(self):
         """
         # 生成一系列的FileModel
         file_models = []
-        count = 10
         lr = None  # 最后一个requirements, str
         lm = None  # 最后一个metadata, dict
         lc = None  # 最后一个config, dict
diff --git a/test/unit/cli/pytest_cli_login.py b/test/unit/cli/pytest_cli_login.py
index 5fb24989..f1bbfd02 100644
--- a/test/unit/cli/pytest_cli_login.py
+++ b/test/unit/cli/pytest_cli_login.py
@@ -7,28 +7,26 @@
 @Description:
     测试命令登录
 """
-
-from swanlab.env import get_swanlab_folder
-from swanlab.package import get_key, get_host_api
+from swanlab.package import get_key
 from click.testing import CliRunner
 from swanlab.cli.main import cli
-from tutils import KEY
+from tutils import TEST_CLOUD_KEY
 from swanlab.error import ValidationError
-import os
+import tutils as T
+import pytest
 
 
 # noinspection PyTypeChecker
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_login_ok():
     runner = CliRunner()
-
-    result = runner.invoke(cli, ["login", "--api-key", KEY])
+    result = runner.invoke(cli, ["login", "--api-key", TEST_CLOUD_KEY])
     assert result.exit_code == 0
-    path = os.path.join(get_swanlab_folder(), ".netrc")
-    assert os.path.exists(path)
-    assert get_key(path, get_host_api())[2] == KEY
+    assert get_key() == TEST_CLOUD_KEY
 
 
 # noinspection PyTypeChecker
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_login_fail():
     runner = CliRunner()
     result = runner.invoke(cli, ["login", "--api-key", "123"])
diff --git a/test/unit/cli/pytest_cli_logout.py b/test/unit/cli/pytest_cli_logout.py
index f8122c00..b15a009c 100644
--- a/test/unit/cli/pytest_cli_logout.py
+++ b/test/unit/cli/pytest_cli_logout.py
@@ -9,30 +9,34 @@
 """
 from click.testing import CliRunner
 from swanlab.cli import cli
-from tutils import KEY
+import tutils as T
+import pytest
 
 
 # noinspection PyTypeChecker
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_logout_ok(monkeypatch):
     runner = CliRunner()
     # 先登录
-    runner.invoke(cli, ["login", "--api-key", KEY])
+    runner.invoke(cli, ["login", "--api-key", T.TEST_CLOUD_KEY])
     monkeypatch.setattr("builtins.input", lambda x: "y")
     result = runner.invoke(cli, ["logout"])
     assert result.exit_code == 0
 
 
 # noinspection PyTypeChecker
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_logout_cancel(monkeypatch):
     runner = CliRunner()
     # 先登录
-    runner.invoke(cli, ["login", "--api-key", KEY])
+    runner.invoke(cli, ["login", "--api-key", T.TEST_CLOUD_KEY])
     monkeypatch.setattr("builtins.input", lambda x: "n")
     result = runner.invoke(cli, ["logout"])
     assert result.exit_code == 0
 
 
 # noinspection PyTypeChecker
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 def test_logout_no_login():
     runner = CliRunner()
     result = runner.invoke(cli, ["logout"])
diff --git a/test/unit/cli/pytest_cli_watch.py b/test/unit/cli/pytest_cli_watch.py
index 18094a38..0a845aca 100644
--- a/test/unit/cli/pytest_cli_watch.py
+++ b/test/unit/cli/pytest_cli_watch.py
@@ -7,95 +7,106 @@
 @Description:
     测试cli的watch命令
 """
-import os
-import time
-import pytest
-from swanlab.cli import cli
-from click.testing import CliRunner
-import multiprocessing
-import requests
-import swanlab
-from tutils import TEMP_PATH
-from swanlab.env import get_swanlog_dir, reset_env, ROOT
-
-
-def mock_swanlog(path=None):
-    if path is None:
-        path = get_swanlog_dir()
-    os.makedirs(path, exist_ok=True)
-    reset_env()
-    swanlab.init(logdir=path, mode="local")
-    swanlab.log({"test": 1})
-    swanlab.finish()
-
-
-# 运行任务
-# noinspection PyTypeChecker
-def runner_watch(*args):
-    runner = CliRunner()
-    return runner.invoke(cli, ["watch", *args])
-
-
-# 测试能否ping通
-def ping(host="127.0.0.1", port=5092):
-    url = f"http://{host}:{port}"  # noqa
-    time.sleep(3)
-    response = requests.get(url, timeout=10)
-    assert response.status_code == 200
-
-
-@pytest.mark.parametrize("logdir, ping_args, args", [
-    # 无参数
-    [None, [], []],
-    # 指定logdir
-    [os.path.join(TEMP_PATH, "watch", "swanlog"), [], ["--logdir", os.path.join(TEMP_PATH, "watch", "swanlog")]],
-    # 指定host和port
-    [None, ["0.0.0.0", "5093"], ["--host", "0.0.0.0", "--port", "5093"]],
-    # 直接watch
-    [os.path.join(TEMP_PATH, "watch", "swanlog"), [], [os.path.join(TEMP_PATH, "watch", "swanlog")]],
-])
-def test_watch_ok(logdir, ping_args, args):
-    """
-    测试watch命令，正常情况
-    """
-    mock_swanlog(logdir)
-    p1 = multiprocessing.Process(target=runner_watch, args=args)
-    p2 = multiprocessing.Process(target=ping, args=ping_args)
-    p1.start()
-    p2.start()
-    p2.join()
-    p1.kill()
-    assert p2.exitcode == 0
-
-
-def test_watch_wrong_logdir():
-    """
-    测试watch命令，logdir不存在
-    """
-    result = runner_watch("wrong_logdir")
-    assert result.exit_code == 2
-    result = runner_watch("--logdir", "wrong_logdir")
-    assert result.exit_code == 2
-    reset_env()
-    os.environ[ROOT] = "wrong_logdir"
-    result = runner_watch()
-    assert result.exit_code == 2
-
-
-def test_watch_wrong_host_port():
-    """
-    测试watch命令，host和port错误
-    """
-    result = runner_watch("--host", "")
-    assert result.exit_code == 6
-    result = runner_watch("--port", "0")
-    assert result.exit_code == 2
-    result = runner_watch("--port", "65536")
-    assert result.exit_code == 2
-    # 如果ip被占用，会报错
-    p1 = multiprocessing.Process(target=runner_watch, args=["--port", "5092"])
-    p1.start()
-    time.sleep(3)
-    result = runner_watch("--port", "5092")
-    assert result.exit_code == 7
-    p1.kill()
+# import os
+# import time
+# import pytest
+# from swanlab.cli import cli
+# from click.testing import CliRunner
+# import multiprocessing
+# import requests
+# import swanlab
+# from tutils import TEMP_PATH
+# from swanlab.env import get_swanlog_dir, SwanLabEnv
+#
+#
+# def mock_swanlog(path=None):
+#     if path is None:
+#         path = get_swanlog_dir()
+#     os.makedirs(path, exist_ok=True)
+#     del os.environ[SwanLabEnv.SWANLOG_FOLDER.value]
+#     swanlab.init(logdir=path, mode="local")
+#     swanlab.log({"test": 1})
+#     swanlab.finish()
+#
+#
+# # 运行任务
+# # noinspection PyTypeChecker
+# def runner_watch(*args):
+#     runner = CliRunner()
+#     return runner.invoke(cli, ["watch", *args])
+#
+#
+# def runner_watch_wrapper(*args):
+#     from tutils import reset_some_env
+#     reset_some_env()
+#     r = runner_watch(*args)
+#     if r.exit_code != 0:
+#         raise Exception(r.output)
+#
+#
+# # 测试能否ping通
+# def ping(host="127.0.0.1", port=5092):
+#     url = f"http://{host}:{port}"  # noqa
+#     time.sleep(3)
+#     try:
+#         response = requests.get(url, timeout=10)
+#         assert response.status_code == 200
+#     except Exception as e:
+#         raise Exception("ping failed", str(e))
+#
+#
+# @pytest.mark.parametrize("logdir, ping_args, args", [
+#     # 无参数
+#     [None, [], []],
+#     # 指定logdir
+#     [os.path.join(TEMP_PATH, "watch", "swanlog"), [], ["--logdir", os.path.join(TEMP_PATH, "watch", "swanlog")]],
+#     # 指定host和port
+#     [None, ["0.0.0.0", "5093"], ["--host", "0.0.0.0", "--port", "5093"]],
+#     # 直接watch
+#     [os.path.join(TEMP_PATH, "watch", "swanlog"), [], [os.path.join(TEMP_PATH, "watch", "swanlog")]],
+# ])
+# def test_watch_ok(logdir, ping_args, args):
+#     """
+#     测试watch命令，正常情况
+#     """
+#     mock_swanlog(logdir)
+#     p1 = multiprocessing.Process(target=runner_watch_wrapper, args=args)
+#     p2 = multiprocessing.Process(target=ping, args=ping_args)
+#     p1.start()
+#     p2.start()
+#     p2.join()
+#     p1.kill()
+#     assert p2.exitcode == 0
+#
+#
+# def test_watch_wrong_logdir():
+#     """
+#     测试watch命令，logdir不存在
+#     """
+#     result = runner_watch("wrong_logdir")
+#     assert result.exit_code == 2
+#     result = runner_watch("--logdir", "wrong_logdir")
+#     assert result.exit_code == 2
+#     os.environ[SwanLabEnv.SWANLOG_FOLDER.value] = "wrong_logdir"
+#     result = runner_watch()
+#     assert result.exit_code == 2
+#
+#
+# def test_watch_wrong_host_port():
+#     """
+#     测试watch命令，host和port错误
+#     """
+#     mock_swanlog()
+#     result = runner_watch("--host", "")
+#     assert result.exit_code == 6
+#     result = runner_watch("--port", "0")
+#     assert result.exit_code == 2
+#     result = runner_watch("--port", "65536")
+#     assert result.exit_code == 2
+#     # 如果ip被占用，会报错
+#     p1 = multiprocessing.Process(target=runner_watch_wrapper, args=[get_swanlog_dir(), "--port", "5092"])
+#     p1.start()
+#     time.sleep(3)
+#     result = runner_watch("--port", "5092")
+#     assert result.exit_code == 7
+#     p1.kill()
diff --git a/test/unit/conftest.py b/test/unit/conftest.py
index b41df11e..af0b9570 100644
--- a/test/unit/conftest.py
+++ b/test/unit/conftest.py
@@ -8,7 +8,7 @@
     配置pytest
 """
 import pytest
-from tutils import clear, init_db, SWANLAB_DIR, SWANLAB_LOG_DIR, PACKAGE_PATH, TEMP_PATH
+from tutils import TEMP_PATH, reset_some_env
 import swanlab.env as E
 import shutil
 import os
@@ -21,9 +21,9 @@ def count_files_in_directory(directory, exclude_prefixes=("__", ".", "~")):
     file_count, folder_count = 0, 0
     for filename in os.listdir(directory):
         file_path = os.path.join(directory, filename)
+        if any(filename.startswith(prefix) for prefix in exclude_prefixes):
+            continue
         if os.path.isfile(file_path):
-            if any(filename.startswith(prefix) for prefix in exclude_prefixes):
-                continue
             file_count += 1
         elif os.path.isdir(file_path):
             folder_count += 1
@@ -34,9 +34,7 @@ def count_files_in_directory(directory, exclude_prefixes=("__", ".", "~")):
 
 
 @pytest.fixture(scope="session", autouse=True)
-def setup_before_all():
-    clear()
-    init_db()
+def setup():
     current_dir = os.path.dirname(os.path.abspath(__file__))
     # 记住当前文件夹下的所有文件数量，用于测试结束后比较，确保测试结束后没有多余文件
     pre_file_count, pre_folder_count = count_files_in_directory(current_dir)
@@ -48,14 +46,17 @@ def setup_before_all():
 
 
 @pytest.fixture(scope="function", autouse=True)
-def setup_before_each():
-    E.reset_env()
-    if os.path.exists(SWANLAB_DIR):
-        shutil.rmtree(SWANLAB_DIR)
+def setup_each():
+    """
+    对每一个测试函数进行设置
+    对于每一个测试函数，将环境变量恢复原状，清空temp文件夹后重新创建一个新的
+    """
+    for key in E.SwanLabEnv.list():
+        if key in os.environ:
+            del os.environ[key]
+    reset_some_env()
+    # 清空temp文件夹
+    if os.path.exists(TEMP_PATH):
+        shutil.rmtree(TEMP_PATH)
+    os.mkdir(TEMP_PATH)
     yield
-    E.reset_env()
-    shutil.rmtree(SWANLAB_DIR, ignore_errors=True)
-    os.environ[E.DEV] = "TRUE"
-    os.environ[E.ROOT] = SWANLAB_LOG_DIR
-    os.environ[E.PACKAGE] = PACKAGE_PATH
-    os.environ[E.HOME] = TEMP_PATH
diff --git a/test/unit/data/modules/pytest_base.py b/test/unit/data/modules/pytest_base.py
deleted file mode 100644
index 30623505..00000000
--- a/test/unit/data/modules/pytest_base.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/6/2 23:36
-@File: test_base.py
-@IDE: pycharm
-@Description:
-    测试基类
-"""
-from swanlab.data.modules.base import BaseType
-
-
-class MockType(BaseType):
-    def __init__(self, value):
-        super().__init__()
-        self.value = value
-
-    def parse(self):
-        return self.value, None
-
-    def get_chart(self):
-        return self.Chart.LINE
-
-    def get_section(self):
-        return "MockType"
-
-    def get_more(self):
-        return {"value": self.value}
-
-    def get_config(self):
-        return {"value": self.value}
-
-
-def test_base():
-    data = MockType(1)
-    assert data.parse() == (1, None)
-    assert data.get_chart() == data.Chart.LINE
-    assert data.get_section() == "MockType"
-    assert data.get_more() == {"value": 1}
-    assert data.get_config() == {"value": 1}
diff --git a/test/unit/utils/pytest_file.py b/test/unit/data/pytest_fomater.py
similarity index 61%
rename from test/unit/utils/pytest_file.py
rename to test/unit/data/pytest_fomater.py
index 00e39af6..da0d4460 100644
--- a/test/unit/utils/pytest_file.py
+++ b/test/unit/data/pytest_fomater.py
@@ -1,21 +1,19 @@
 #!/usr/bin/env python
 # -*- coding: utf-8 -*-
 r"""
-@DATE: 2024/4/28 16:21
-@File: pytest_file.py
+@DATE: 2024/6/19 14:43
+@File: pytest_formater.py
 @IDE: pycharm
 @Description:
-    测试文件格式函数
+    
 """
 import pytest
 from nanoid import generate
 
 # noinspection PyProtectedMember
-from swanlab.utils.file import (
+from swanlab.data.formater import (
     check_proj_name_format,
-    check_exp_name_format,
     _auto_cut,
-    check_desc_format,
     check_key_format,
 )
 
@@ -114,105 +112,6 @@ def test_proj_name_no_cut(self, value: str):
             check_proj_name_format(value, auto_cut=False)
 
 
-class TestExpName:
-    @pytest.mark.parametrize(
-        "value",
-        [
-            generate(size=100),
-            generate(size=1),
-            "-",
-            "_",
-        ],
-    )
-    def test_exp_name_common(self, value):
-        """
-        测试正常情况
-        """
-        assert check_exp_name_format(value) == value
-
-    @pytest.mark.parametrize("value", [None, 1, [], {}])
-    def test_exp_name_type_error(self, value: str):
-        """
-        测试类型错误
-        """
-        with pytest.raises(TypeError):
-            check_exp_name_format(value)
-
-    @pytest.mark.parametrize("value", ["啊哈哈", "&^%", "/;]x]", generate(size=100), "👨‍💻👨‍💻👨‍💻👨‍💻👨‍"])
-    def test_exp_name_value_special(self, value: str):
-        """
-        测试特殊值
-        """
-        assert check_exp_name_format(value) == value
-
-    @pytest.mark.parametrize(
-        "value",
-        [
-            "",
-            "   ",
-        ],
-    )
-    def test_exp_name_value_empty(self, value: str):
-        """
-        测试空值
-        """
-        with pytest.raises(ValueError):
-            check_exp_name_format(value)
-
-    @pytest.mark.parametrize(
-        "value",
-        [
-            generate(size=101),
-            generate(size=1000),
-            generate(size=10000),
-        ],
-    )
-    def test_exp_name_auto_cut(self, value: str):
-        """
-        测试自动截断
-        """
-        assert len(check_exp_name_format(value)) == 100
-
-    @pytest.mark.parametrize(
-        "value",
-        [
-            generate(size=101),
-            generate(size=1000),
-            generate(size=10000),
-        ],
-    )
-    def test_exp_name_no_cut(self, value: str):
-        """
-        测试不自动截断
-        """
-        with pytest.raises(IndexError):
-            check_exp_name_format(value, auto_cut=False)
-
-
-class TestDesc:
-    @pytest.mark.parametrize("value", [generate(size=100), generate(size=1), "-", "_", "👾👾👾👾👾👾"])
-    def test_desc_common(self, value):
-        """
-        测试正常情况
-        """
-        assert check_desc_format(value) == value
-
-    @pytest.mark.parametrize("value", [None, 1, [], {}])
-    def test_desc_type_error(self, value: str):
-        """
-        测试类型错误
-        """
-        with pytest.raises(TypeError):
-            check_desc_format(value)
-
-    @pytest.mark.parametrize("value", ["", "   ", " " * 256])
-    def test_desc_value_empty(self, value: str):
-        """
-        测试空值
-        """
-        assert check_desc_format(value) == ""
-
-
 class TestTag:
     @pytest.mark.parametrize(
         "value", [generate(size=255), generate(size=100), generate(size=1), "12/", "-", "_", "👾👾👾👾👾👾"]
@@ -251,7 +150,7 @@ def test_tag_type_error(self, value: str):
     @pytest.mark.parametrize("value", ["", "   ", " " * 256, ".sas", "/asa"])
     def test_tag_value_error(self, value: str):
         """
-        测试空值
+        测试不合法值
         """
         with pytest.raises(ValueError):
             check_key_format(value)
diff --git a/test/unit/data/pytest_sdk.py b/test/unit/data/pytest_sdk.py
index b3cdbe3c..a138a748 100644
--- a/test/unit/data/pytest_sdk.py
+++ b/test/unit/data/pytest_sdk.py
@@ -7,16 +7,11 @@
 @Description:
     测试sdk的一些api
 """
-from swanlab.env import (
-    get_swanlab_folder,
-    MODE,
-    ROOT,
-    HOME
-)
 import tutils as T
 import swanlab.data.sdk as S
-import swanlab.error as E
+import swanlab.error as Err
 from swanlab.log import swanlog
+from swanlab.env import SwanLabEnv, get_save_dir
 from swanlab.data.run import get_run
 from nanoid import generate
 import pytest
@@ -30,13 +25,13 @@ def setup_function():
     """
     swanlog.disable_log()
     yield
-    # 恢复原状
+    run = get_run()
+    if run is not None:
+        run.finish()
     swanlog.enable_log()
-    if get_run() is not None:
-        get_run().finish()
-    os.environ[ROOT] = T.SWANLAB_LOG_DIR
-    if HOME in os.environ:
-        del os.environ[HOME]
+
+
+MODE = SwanLabEnv.SWANLAB_MODE.value
 
 
 class TestInitMode:
@@ -58,8 +53,9 @@ def test_init_local(self):
         run.log({"TestInitMode": 1})  # 不会报错
         assert get_run() is not None
 
+    @pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
     def test_init_cloud(self):
-        S.login(T.KEY)
+        S.login(T.TEST_CLOUD_KEY)
         run = S.init(mode="cloud")
         assert os.environ[MODE] == "cloud"
         run.log({"TestInitMode": 1})  # 不会报错
@@ -87,9 +83,10 @@ def test_init_local_env(self):
         assert os.environ[MODE] == "local"
         run.log({"TestInitMode": 1})
 
+    @pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
     def test_init_cloud_env(self):
         os.environ[MODE] = "cloud"
-        S.login(T.KEY)
+        S.login(T.TEST_CLOUD_KEY)
         run = S.init()
         assert os.environ[MODE] == "cloud"
         run.log({"TestInitMode": 1})
@@ -127,6 +124,9 @@ def test_init_project(self):
         assert run.project_name == project
 
 
+LOG_DIR = SwanLabEnv.SWANLOG_FOLDER.value
+
+
 class TestInitLogdir:
     """
     测试init时函数的logdir参数设置行为
@@ -139,9 +139,9 @@ def test_init_logdir_disabled(self):
         logdir = generate()
         run = S.init(logdir=logdir, mode="disabled")
         assert run.settings.swanlog_dir != logdir
-        assert run.settings.swanlog_dir == os.environ[ROOT]
+        assert run.settings.swanlog_dir == os.environ[LOG_DIR]
         run.finish()
-        del os.environ[ROOT]
+        del os.environ[LOG_DIR]
         run = S.init(logdir=logdir, mode="disabled")
         assert run.settings.swanlog_dir != logdir
         assert run.settings.swanlog_dir == os.path.join(os.getcwd(), "swanlog")
@@ -154,7 +154,7 @@ def test_init_logdir_enabled(self):
         run = S.init(logdir=logdir, mode="local")
         assert run.settings.swanlog_dir == logdir
         run.finish()
-        del os.environ[ROOT]
+        del os.environ[LOG_DIR]
         logdir = os.path.join(T.TEMP_PATH, generate()).__str__()
         run = S.init(logdir=logdir, mode="local")
         assert run.settings.swanlog_dir == logdir
@@ -164,40 +164,49 @@ def test_init_logdir_env(self):
         通过环境变量设置logdir
         """
         logdir = os.path.join(T.TEMP_PATH, generate()).__str__()
-        os.environ[ROOT] = logdir
+        os.environ[LOG_DIR] = logdir
         run = S.init(mode="local")
         assert run.settings.swanlog_dir == logdir
         run.finish()
-        del os.environ[ROOT]
+        del os.environ[LOG_DIR]
         logdir = os.path.join(T.TEMP_PATH, generate()).__str__()
-        os.environ[ROOT] = logdir
+        os.environ[LOG_DIR] = logdir
         run = S.init(mode="local")
         assert run.settings.swanlog_dir == logdir
 
 
+@pytest.mark.skipif(T.TEST_CLOUD_SKIP, reason="skip cloud test")
 class TestLogin:
     """
     测试通过sdk封装的login函数登录
     不填apikey的部分不太好测
     """
 
+    @staticmethod
+    def get_password(prompt: str):
+        # 如果是第一次登录，使用错误的key，会提示重新输入
+        if "Paste" in prompt:
+            return generate()
+        else:
+            return T.TEST_CLOUD_KEY
+
     def test_use_home_key(self, monkeypatch):
         """
         使用家目录下的key，不需要输入
         如果家目录下的key获取失败，会使用getpass.getpass要求用户输入，作为测试，使用monkeypatch替换getpass.getpass
         """
-        os.environ[HOME] = T.TEMP_PATH
-        monkeypatch.setattr("getpass.getpass", T.get_password)
+        os.environ[LOG_DIR] = T.TEMP_PATH
+        monkeypatch.setattr("getpass.getpass", self.get_password)
         S.login()
         # 默认保存Key
-        assert os.path.exists(os.path.join(get_swanlab_folder(), ".netrc"))
+        assert os.path.exists(os.path.join(get_save_dir(), ".netrc"))
 
     def test_use_input_key(self, monkeypatch):
         """
         使用输入的key
         """
         key = generate()
-        with pytest.raises(E.ValidationError):
+        with pytest.raises(Err.ValidationError):
             S.login(api_key=key)
-        key = T.KEY
+        key = T.TEST_CLOUD_KEY
         S.login(api_key=key)
diff --git a/test/unit/data/run/pytest_config.py b/test/unit/data/run/pytest_config.py
index 17a012db..bf4cd94e 100644
--- a/test/unit/data/run/pytest_config.py
+++ b/test/unit/data/run/pytest_config.py
@@ -2,7 +2,6 @@
 import yaml
 from swanlab.data.run.main import SwanLabRun, get_run, swanlog, get_config
 from swanlab.data.run.config import SwanLabConfig, parse, Line, RuntimeInfo, MutableMapping
-from tutils import clear
 import pytest
 import omegaconf
 import argparse
@@ -17,7 +16,6 @@ def setup_function():
         get_run().finish()
     swanlog.disable_log()
     yield
-    clear()
     swanlog.enable_log()
 
 
diff --git a/test/unit/data/run/pytest_main.py b/test/unit/data/run/pytest_main.py
index 01ccfb52..d0d25bc2 100644
--- a/test/unit/data/run/pytest_main.py
+++ b/test/unit/data/run/pytest_main.py
@@ -11,7 +11,7 @@
 from swanlab.data.run.main import SwanLabRun, get_run, SwanLabRunState, swanlog
 from swanlab import Image, Audio, Text
 from nanoid import generate
-from tutils import clear, TEMP_PATH
+from tutils import TEMP_PATH
 from PIL import Image as PILImage
 import soundfile as sf
 import numpy as np
@@ -30,7 +30,6 @@ def setup_function():
         get_run().finish()
     swanlog.disable_log()
     yield
-    clear()
     swanlog.enable_log()
 
 
diff --git a/test/unit/log/pytest_log.py b/test/unit/log/pytest_log.py
index e337146d..b457c799 100644
--- a/test/unit/log/pytest_log.py
+++ b/test/unit/log/pytest_log.py
@@ -5,7 +5,7 @@
 @File: pytest_log.py
 @IDE: pycharm
 @Description:
-    测试swanlog类
+    测试swanlog类，只需测试其日志监听功能
 """
 import pytest
 from swanlab.log import swanlog
@@ -25,29 +25,17 @@ def before_test_global_swanlog():
         pass
 
 
-# 创建console文件夹
-console_dir = os.path.join(TEMP_PATH, str(generate()))
-if not os.path.exists(console_dir):
-    os.mkdir(console_dir)
-
-
 class TestSwanLogInstall:
     """
     目前在设计上不希望外界实例化SwanLog，所以不提供实例化测试
     """
 
-    # def test_install_success(self):
-    #     lg = SwanLog('tmp')
-    #     lg.install()
-    #     assert lg.installed is True
-    #     lg.uninstall()
-
-    # def test_install_duplicate(self):
-    #     lg = SwanLog('tmp')
-    #     lg.install()
-    #     with pytest.raises(RuntimeError) as e:
-    #         lg.install()
-    #     assert str(e.value) == 'SwanLog has been installed'
+    @staticmethod
+    def create_console_dir():
+        # 创建console文件夹
+        console_dir = os.path.join(TEMP_PATH, str(generate()))
+        os.mkdir(console_dir)
+        return console_dir
 
     def test_global_install(self):
         swanlog.install()
@@ -60,6 +48,7 @@ def test_global_install(self):
         assert swanlog.installed is True
 
     def test_write_after_uninstall(self):
+        console_dir = self.create_console_dir()
         swanlog.install(console_dir)
         swanlog.uninstall()
         # 加一行防止其他问题
@@ -75,6 +64,7 @@ def test_write_after_uninstall(self):
             assert len(content) == 0
 
     def test_write_to_file(self):
+        console_dir = self.create_console_dir()
         swanlog.install(console_dir)
         # 加一行防止其他问题
         print("\ntest write to file")
@@ -91,6 +81,7 @@ def test_write_to_file(self):
             assert content[-1] == b + "\n"
 
     def test_write_logging_to_file(self):
+        console_dir = self.create_console_dir()
         swanlog.install(console_dir, log_level="debug")
         # 加一行防止其他问题
         print("\ntest write to file")
@@ -102,10 +93,11 @@ def test_write_logging_to_file(self):
         assert len(files) == 1
         with open(os.path.join(console_dir, files[0]), "r") as f:
             content = f.readlines()
-            assert content[-2] == "swanlab:" + " " + a + "\n"
-            assert content[-1] == "swanlab:" + " " + b + "\n"
+            assert content[-2] == "swanlab: " + a + "\n"
+            assert content[-1] == "swanlab: " + b + "\n"
 
     def test_can_write_logging(self):
+        console_dir = self.create_console_dir()
         swanlog.install(console_dir)
         # 加一行防止其他问题
         print("\ntest write to file")
@@ -117,5 +109,5 @@ def test_can_write_logging(self):
         assert len(files) == 1
         with open(os.path.join(console_dir, files[0]), "r") as f:
             content = f.readlines()
-            assert content[-2] != "swanlab:" + " " + a + "\n"
-            assert content[-1] == "swanlab:" + " " + b + "\n"
+            assert content[-2] != "swanlab: " + a + "\n"
+            assert content[-1] == "swanlab: " + b + "\n"
diff --git a/test/unit/pytest_env.py b/test/unit/pytest_env.py
deleted file mode 100644
index 4b44f4c7..00000000
--- a/test/unit/pytest_env.py
+++ /dev/null
@@ -1,231 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/5/24 20:25
-@File: pytest_env.py
-@IDE: pycharm
-@Description:
-    测试swanlab/env.py中的环境变量和工具函数
-"""
-from swanlab.env import SwanLabMode, get_mode, reset_env, MODE
-import swanlab.env as E
-from nanoid import generate
-from tutils import SWANLAB_LOG_DIR, PACKAGE_PATH, TEMP_PATH
-import pytest
-import os
-
-
-@pytest.fixture(scope="function", autouse=True)
-def setup_function():
-    """
-    在当前测试文件下的每个测试函数执行前后执行
-    """
-    if MODE in os.environ:
-        del os.environ[MODE]
-    if E.ROOT in os.environ:
-        del os.environ[E.ROOT]
-    if E.PACKAGE in os.environ:
-        del os.environ[E.PACKAGE]
-    if E.DEV in os.environ:
-        del os.environ[E.DEV]
-    yield
-    # 恢复原状
-    if MODE in os.environ:
-        del os.environ[MODE]
-
-
-def use_strict_mode():
-    """
-    使用严格模式
-    """
-    get_mode({MODE: SwanLabMode.CLOUD.value})
-
-
-def use_not_strict_mode():
-    """
-    使用非严格模式
-    """
-    get_mode({MODE: SwanLabMode.DISABLED.value})
-
-
-class TestStrictMode:
-    """
-    同时测试get_mode和is_strict_mode函数
-    """
-
-    def test_strict_mode(self):
-        """
-        测试默认严格模式
-        """
-        assert E.is_strict_mode() is True
-
-    def test_strict_mode_cloud(self):
-        """
-        测试严格模式
-        """
-        use_strict_mode()
-        assert E.is_strict_mode() is True
-
-    def test_strict_mode_disabled(self):
-        """
-        测试非严格模式
-        """
-        use_not_strict_mode()
-        assert E.is_strict_mode() is False
-
-    def test_strict_mode_local(self):
-        """
-        测试本地模式
-        """
-        use_strict_mode()
-        assert E.is_strict_mode() is True
-
-    def test_strict_mode_error(self):
-        """
-        测试重定向模式
-        """
-        with pytest.raises(ValueError):
-            get_mode({MODE: generate()})
-
-
-class TestAssertExist:
-    """
-    测试assert_exist函数的正确性，由于assert_exist函数有5个参数，除了第一个参数以外其他参数会影响返回值，所以需要分情况测试
-    非严格模式与严格模式隔离，因此可以分开测试
-    """
-
-    def test_exist_non_strict(self):
-        """
-        非严格模式下的文件存在断言测试
-        """
-        use_not_strict_mode()
-        assert E.assert_exist(__file__) is True
-        assert E.assert_exist(generate()) is False
-
-    # ---------------------------------- 严格模式测试 ----------------------------------
-
-    def test_exist_ok(self):
-        """
-        测试严格模式下，文件路径存在
-        """
-        assert E.assert_exist(__file__) is True
-        assert E.assert_exist(__file__, target_type="file") is True
-        with pytest.raises(NotADirectoryError):
-            E.assert_exist(__file__, target_type="folder")
-        with pytest.raises(IsADirectoryError):
-            E.assert_exist(os.path.dirname(__file__), target_type="file")
-        with pytest.raises(FileNotFoundError):
-            E.assert_exist(generate())
-
-    def test_exist_ra_false(self):
-        """
-        测试严格模式下，文件路径不存在，但是不抛出异常
-        """
-        assert E.assert_exist(generate(), ra=False) is False
-        assert E.assert_exist(generate(), target_type="file", ra=False) is False
-        assert E.assert_exist(generate(), target_type="folder", ra=False) is False
-        # 文件路径存在，但是设置了ra=False和target_type
-        with pytest.raises(NotADirectoryError):
-            E.assert_exist(__file__, target_type="folder", ra=False)
-        with pytest.raises(IsADirectoryError):
-            E.assert_exist(os.path.dirname(__file__), target_type="file", ra=False)
-
-
-def test_env_reset():
-    os.environ[E.ROOT] = SWANLAB_LOG_DIR
-    E.init_env()
-    assert len(E._env) > 0
-    reset_env()
-    assert len(E._env) == 0
-
-
-def test_is_dev():
-    # 默认为False
-    assert E.is_dev() is False
-    reset_env()
-    # 设置为True
-    os.environ[E.DEV] = "TRUE"
-    assert E.is_dev() is True
-    reset_env()
-    # 大小写敏感
-    os.environ[E.DEV] = "true"
-    assert E.is_dev() is False
-
-
-class TestSwanLogDir:
-
-    def test_default(self):
-        """
-        默认情况下日志目录应该存放在当前运行命令时的swanlog目录
-        """
-        use_not_strict_mode()
-        assert E.get_swanlog_dir() == os.path.join(os.getcwd(), "swanlog")
-
-    def test_use_env(self):
-        """
-        测试使用环境变量
-        """
-        os.environ[E.ROOT] = SWANLAB_LOG_DIR  # 在启动时已保证此目录存在
-        assert E.get_swanlog_dir() == SWANLAB_LOG_DIR
-        # 必须是一个绝对路径
-        reset_env()
-        del os.environ[E.ROOT]
-        os.environ[E.ROOT] = generate()
-        with pytest.raises(ValueError):
-            E.get_swanlog_dir()
-
-    def test_notfound_strict(self):
-        """
-        测试严格模式下目录不存在
-        """
-        use_strict_mode()
-        with pytest.raises(FileNotFoundError):
-            E.get_swanlog_dir()
-
-
-class TestPackagePath:
-    def test_default(self):
-        """
-        测试默认的package路径
-        """
-        project_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
-        package_path = os.path.join(project_dir, "swanlab", "package.json")
-        assert E.get_package_path() == package_path
-
-    def test_use_env(self):
-        """
-        非开发模式下使用环境变量
-        """
-        project_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
-        package_path = os.path.join(project_dir, "swanlab", "package.json")
-        os.environ[E.PACKAGE] = generate()
-        assert E.get_package_path() != os.environ[E.PACKAGE]
-        assert E.get_package_path() == package_path
-
-    def test_use_env_dev(self):
-        """
-        开发模式下使用环境变量
-        """
-        os.environ[E.DEV] = "TRUE"
-        os.environ[E.PACKAGE] = generate()
-        assert E.get_package_path() == os.environ[E.PACKAGE]
-        # 需要注意的是测试时已经引入了swanlab包，而其默认执行了get_package_path函数
-        from swanlab.package import package_path
-
-        assert E.get_package_path() != package_path
-        assert package_path == PACKAGE_PATH
-
-
-class TestGetSwanLabFolder:
-    """
-    测试获取.swanlab目录路径
-    """
-
-    def test_use_env(self):
-        os.environ[E.HOME] = TEMP_PATH
-        assert TEMP_PATH not in E.get_swanlab_folder()
-
-    def test_use_env_dev(self):
-        os.environ[E.DEV] = "TRUE"
-        os.environ[E.HOME] = TEMP_PATH
-        assert E.get_swanlab_folder() == os.path.join(TEMP_PATH, ".swanlab")
diff --git a/test/unit/pytest_example.py b/test/unit/pytest_example.py
deleted file mode 100644
index 56761d85..00000000
--- a/test/unit/pytest_example.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024-04-03 00:08:50
-@File: test/unit/pytest_example.py
-@IDE: vscode
-@Description:
-    测试用例示例，所有测试用例文件以pytest_开头
-"""
-
-
-def add(x, y):
-    """简单的加法函数"""
-    return x + y
-
-
-def test_addition():
-    """测试加法函数"""
-    assert add(3, 5) == 8
-    assert add(-1, 1) == 0
-    assert add(2.5, 2.5) == 5
-
-
-def subtract(x, y):
-    """简单的减法函数"""
-    return x - y
-
-
-def test_subtraction():
-    """测试减法函数"""
-    assert subtract(5, 3) == 2
-    assert subtract(1, -1) == 2
-    assert subtract(2.5, 1) == 1.5
diff --git a/test/unit/pytest_package.py b/test/unit/pytest_package.py
index 6d81f6c7..3a5e74df 100644
--- a/test/unit/pytest_package.py
+++ b/test/unit/pytest_package.py
@@ -1,19 +1,13 @@
-from tutils.config import nanoid
+import pytest
+import swanlab.package as P
+from swanlab.env import SwanLabEnv, get_save_dir
+import nanoid
+import netrc
 import json
-from swanlab.package import (
-    get_package_version,
-    get_host_web,
-    get_user_setting_path,
-    get_host_api,
-    get_project_url,
-    get_experiment_url
-)
 import os
-from swanlab.package import get_package_latest_version
 
-PACKAGE_PATH = os.environ["SWANLAB_PACKAGE_PATH"]
-
-package_data = json.load(open(PACKAGE_PATH))
+_ = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+package_data = json.load(open(os.path.join(_, "swanlab", "package.json")))
 
 
 def test_package_latest_version():
@@ -21,64 +15,41 @@ def test_package_latest_version():
     测试获取swanlab最新版本号
     """
     # 正常情况
-    assert isinstance(get_package_latest_version(), str)
+    assert isinstance(P.get_package_latest_version(), str)
     # 超时情况
-    assert get_package_latest_version(timeout=1e-3) is None
-
-
-def mock_get_package_version():
-    return get_package_version(PACKAGE_PATH)
-
-
-def mock_get_host_web():
-    return get_host_web(PACKAGE_PATH)
-
-
-def mock_get_host_api():
-    return get_host_api(PACKAGE_PATH)
-
-
-def mock_get_user_setting_path():
-    return get_user_setting_path(PACKAGE_PATH)
-
-
-def mock_get_project_url(username: str, projname: str):
-    return get_project_url(username, projname, PACKAGE_PATH)
-
-
-def mock_get_experiment_url(username: str, projname: str, expid: str):
-    return get_experiment_url(username, projname, expid, PACKAGE_PATH)
-
-
-# ---------------------------------- 简单测试一下 ----------------------------------
+    assert P.get_package_latest_version(timeout=1e-3) is None
 
 
 def test_get_package_version():
     """
     测试获取版本号
     """
-    assert mock_get_package_version() == package_data["version"]
+    assert P.get_package_version() == os.getenv(SwanLabEnv.SWANLAB_VERSION.value)
+    del os.environ[SwanLabEnv.SWANLAB_VERSION.value]
+    assert P.get_package_version() == package_data["version"]
 
 
-def test_get_host_web():
+def test_get_host_web_env():
     """
-    测试获取web地址
+    通过环境变量指定web地址
     """
-    assert mock_get_host_web() == package_data["host"]["web"]
+    os.environ[SwanLabEnv.SWANLAB_WEB_HOST.value] = nanoid.generate()
+    assert P.get_host_web() == os.environ[SwanLabEnv.SWANLAB_WEB_HOST.value]
 
 
-def test_get_host_api():
+def test_get_host_api_env():
     """
-    测试获取api地址
+    通过环境变量指定api地址
     """
-    assert mock_get_host_api() == package_data["host"]["api"]
+    os.environ[SwanLabEnv.SWANLAB_API_HOST.value] = nanoid.generate()
+    assert P.get_host_api() == os.environ[SwanLabEnv.SWANLAB_API_HOST.value]
 
 
 def test_get_user_setting_path():
     """
     测试获取用户设置文件路径
     """
-    assert mock_get_user_setting_path() == mock_get_host_web() + "/settings"
+    assert P.get_user_setting_path() == P.get_host_web() + "/settings"
 
 
 def test_get_project_url():
@@ -87,7 +58,7 @@ def test_get_project_url():
     """
     username = nanoid.generate()
     projname = nanoid.generate()
-    assert mock_get_project_url(username, projname) == mock_get_host_web() + "/" + username + "/" + projname
+    assert P.get_project_url(username, projname) == P.get_host_web() + "/" + username + "/" + projname
 
 
 def test_get_experiment_url():
@@ -97,5 +68,99 @@ def test_get_experiment_url():
     username = nanoid.generate()
     projname = nanoid.generate()
     expid = nanoid.generate()
-    assert (mock_get_experiment_url(username, projname, expid)
-            == mock_get_host_web() + "/" + username + "/" + projname + "/" + expid)
+    assert P.get_experiment_url(
+        username, projname,
+        expid
+    ) == P.get_host_web() + "/" + username + "/" + projname + "/" + expid
+
+
+# ---------------------------------- 登录部分 ----------------------------------
+
+class TestGetKey:
+
+    def test_ok(self):
+        """
+        获取key成功
+        """
+        # 首先需要登录
+        file = os.path.join(get_save_dir(), ".netrc")
+        with open(file, "w"):
+            pass
+        nrc = netrc.netrc(file)
+        key = nanoid.generate()
+        nrc.hosts[P.get_host_api()] = ("user", "", key)
+        with open(file, "w") as f:
+            f.write(nrc.__repr__())
+        assert P.get_key() == key
+
+    def test_no_file(self):
+        """
+        文件不存在
+        """
+        from swanlab.error import KeyFileError
+        with pytest.raises(KeyFileError) as e:
+            P.get_key()
+        assert str(e.value) == "The file does not exist"
+
+    def test_no_host(self):
+        from swanlab.error import KeyFileError
+        self.test_ok()
+        host = nanoid.generate()
+        os.environ[SwanLabEnv.SWANLAB_API_HOST.value] = host
+        assert P.get_host_api() == host
+        with pytest.raises(KeyFileError) as e:
+            P.get_key()
+        assert str(e.value) == f"The host {host} does not exist"
+
+
+class TestSaveKey:
+
+    @staticmethod
+    def get_key(path, host):
+        nrc = netrc.netrc(path)
+        info = nrc.authenticators(host)
+        return info[2]
+
+    def test_ok(self):
+        """
+        保存key成功
+        """
+        path = os.path.join(get_save_dir(), ".netrc")
+        password = nanoid.generate()
+        host = P.get_host_api()
+        P.save_key("user", password, host=host)
+        assert self.get_key(path, host) == password
+
+
+class TestIsLogin:
+    @staticmethod
+    def login():
+        path = os.path.join(get_save_dir(), ".netrc")
+        with open(path, "w"):
+            pass
+        nrc = netrc.netrc(path)
+        key = nanoid.generate()
+        nrc.hosts[P.get_host_api()] = ("user", "", key)
+        with open(path, "w") as f:
+            f.write(nrc.__repr__())
+
+    def test_ok(self):
+        """
+        已经登录
+        """
+        self.login()
+        assert P.is_login()
+
+    def test_no_file(self):
+        """
+        文件不存在
+        """
+        assert not P.is_login()
+
+    def test_wrong_host(self):
+        """
+        host不匹配
+        """
+        self.login()
+        os.environ[SwanLabEnv.SWANLAB_API_HOST.value] = nanoid.generate()
+        assert not P.is_login()
diff --git a/test/unit/utils/pytest_key.py b/test/unit/utils/pytest_key.py
deleted file mode 100644
index a478918c..00000000
--- a/test/unit/utils/pytest_key.py
+++ /dev/null
@@ -1,101 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/4/3 01:21
-@File: pytest_key.py
-@IDE: pycharm
-@Description:
-    token测试
-"""
-import os.path
-import pytest
-from swanlab.error import KeyFileError
-from swanlab.utils.key import save_key, get_key
-from tutils.config import TEMP_PATH, nanoid
-from typing import Tuple
-import netrc
-
-netrc_pah = os.path.join(TEMP_PATH, ".netrc")
-
-
-# ---------------------------------- 工具函数 ----------------------------------
-
-def create_key() -> Tuple[str, str, str]:
-    """
-    生成保存key所必须的一些参数，host，key、password
-    """
-    return nanoid.generate(size=8), nanoid.generate(size=11), nanoid.generate()
-
-
-def mock_key():
-    """
-    模拟存储一个key
-    """
-    info = create_key()
-    nrc = netrc.netrc(netrc_pah)
-    nrc.hosts[info[0]] = (info[1], None, info[2])
-    with open(netrc_pah, "w") as f:
-        f.write(nrc.__repr__())
-    return info
-
-
-def _test_save_key(info, path=netrc_pah):
-    save_key(path, *info)
-
-
-# ---------------------------------- 测试 ----------------------------------
-
-def test_save_key_success():
-    """
-    测试保存key成功
-    """
-    info = create_key()
-    _test_save_key(info)
-    # netrc读取
-    nrc = netrc.netrc(netrc_pah).authenticators(info[0])
-    assert nrc[0] == info[1]
-    assert nrc[1] == '' or nrc[1] is None
-    assert nrc[2] == info[2]
-
-
-def test_save_key_path_error():
-    """
-    测试保存key时，输入的path上级文件夹不存在的情况
-    """
-    _netrc_pah = os.path.join(os.path.dirname(netrc_pah), nanoid.generate(), os.path.basename(netrc_pah))
-    info = create_key()
-    with pytest.raises(KeyFileError) as e:
-        _test_save_key(info, _netrc_pah)
-    assert str(e.value) == "The parent folder does not exist"
-
-
-def test_save_key_name_error():
-    """
-    测试保存key时，输入的path文件名不是.netrc的情况
-    """
-    _netrc_pah = os.path.join(TEMP_PATH, nanoid.generate())
-    info = create_key()
-    with pytest.raises(KeyFileError) as e:
-        _test_save_key(info, str(_netrc_pah))
-    assert str(e.value) == "The file name must be .netrc"
-
-
-def test_get_key_success():
-    """
-    测试获取key成功
-    """
-    info = mock_key()
-    nrc = get_key(netrc_pah, info[0])
-    assert nrc[0] == info[1]
-    assert nrc[1] == '' or nrc[1] is None
-    assert nrc[2] == info[2]
-
-
-def test_get_key_path_error():
-    """
-    测试获取key时，输入的path文件不存在的情况
-    """
-    _netrc_pah = os.path.join(TEMP_PATH, nanoid.generate())
-    with pytest.raises(KeyFileError) as e:
-        get_key(str(_netrc_pah), nanoid.generate())
-    assert str(e.value) == "The file does not exist"
diff --git a/test/use_swanlog.py b/test/use_swanlog.py
deleted file mode 100644
index f070d23b..00000000
--- a/test/use_swanlog.py
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-r"""
-@DATE: 2024/5/16 17:38
-@File: use_swanlog.py
-@IDE: pycharm
-@Description:
-    使用swanlog打印日志
-"""
-from swanlab.log import swanlog
-
-SHOW = "这条日志应该显示"
-HIDE = "这条日志不应该显示"
-
-# ---------------------------------- 初始情况 ----------------------------------
-
-swanlog.debug(HIDE)
-swanlog.info(SHOW)
-swanlog.warning(SHOW)
-swanlog.error(SHOW)
-swanlog.critical(SHOW)
-
-# ---------------------------------- 安装日志系统 ----------------------------------
-
-swanlog.install(log_level="debug")
-
-swanlog.debug(SHOW)
-
-# ---------------------------------- 卸载日志系统 ----------------------------------
-
-swanlog.uninstall()
-
-swanlog.debug(HIDE)