Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .idea/dictionaries/project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion swanlab/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
SwanLabEnv.check()

# 导出 OpenApi 接口,必须要等待上述的 import 语句执行完毕以后才能导出,否则会触发循环引用
from .api import OpenApi
from .api import OpenApi, Api

__version__ = get_package_version()

Expand All @@ -48,6 +48,7 @@
"get_config",
"config",
"OpenApi",
"Api",
"sync_wandb",
"sync_mlflow",
"sync_tensorboardX",
Expand Down
176 changes: 164 additions & 12 deletions swanlab/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,167 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
r"""
@DATE: 2025/4/29 9:40
@File: __init__.py
@IDE: pycharm
@Description:
SwanLab OpenAPI包
"""
@author: Zhou QiYang
@file: __init__.py
@time: 2026/1/5 17:58
@description: SwanLab OpenAPI包
"""

from typing import Optional, List, Dict

from swanlab.core_python import auth, Client
from swanlab.core_python.api.experiment import get_single_experiment, get_project_experiments
from swanlab.error import KeyFileError
from swanlab.log import swanlog
from swanlab.package import HostFormatter, get_key
from .deprecated import OpenApi
from .experiment import Experiment
from .experiments import Experiments
from .project import Project
from .projects import Projects
from .user import User
from .users import Users
from .utils import self_hosted
from .workspace import Workspace
from .workspaces import Workspaces
from ..core_python.api.project import get_project_info
from ..core_python.api.user import get_workspace_info


class Api:
def __init__(self, api_key: Optional[str] = None, host: Optional[str] = None, web_host: Optional[str] = None):
"""
初始化 OpenApi 实例,用户需提前登录,或者提供API密钥
:param api_key: API 密钥,可选
:param host: API 主机地址,可选
:param web_host: Web 主机地址,可选
"""
if host or web_host:
HostFormatter(host, web_host)()
if api_key:
swanlog.debug("Using API key", api_key)
else:
swanlog.debug("Using existing key")
try:
api_key = get_key()
except KeyFileError as e:
swanlog.error("To use SwanLab OpenAPI, please login first.")
raise RuntimeError("Not logged in.") from e

self._login_info = auth.code_login(api_key, save_key=False)
# 一个OpenApi对应一个client,可创建多个api获取从不同的client获取不同账号下的实验信息
self._client: Client = Client(self._login_info)
self._web_host = self._login_info.web_host
self._login_user = self._login_info.username

def user(self, username: str = None) -> User:
"""
获取用户实例,用于操作用户相关信息
:param username: 指定用户名,如果为 None,则返回当前登录用户
:return: User 实例,可对当前/指定用户进行操作
"""
return User(client=self._client, login_user=self._login_user, username=username)

@self_hosted("root")
def users(self) -> Users:
"""
超级管理员获取所有用户
:return: User 实例,可对当前/指定用户进行操作
"""
return Users(self._client, login_user=self._login_user)

def projects(
self,
path: str,
sort: Optional[List[str]] = None,
search: Optional[str] = None,
detail: Optional[bool] = True,
) -> Projects:
"""
获取指定工作空间(组织)下的所有项目信息
:param path: 工作空间(组织)名称 'username'
:param sort: 排序方式,可选
:param search: 搜索关键词,可选
:param detail: 是否返回详细信息,可选
:return: Projects 实例,可遍历获取项目信息
"""
return Projects(
self._client,
web_host=self._web_host,
path=path,
sort=sort,
search=search,
detail=detail,
)

def project(
self,
path: str,
) -> Project:
"""
获取指定工作空间(组织)下的指定项目信息
:param path: 项目路径 'username/project'
:return: Project 实例,单个项目的信息
"""
data = get_project_info(self._client, path=path)
return Project(self._client, web_host=self._web_host, data=data)

def runs(self, path: str, filters: Dict[str, object] = None) -> Experiments:
"""
获取指定项目下的所有实验信息
:param path: 项目路径,格式为 'username/project'
:return: Experiments 实例,可遍历获取实验信息
:param filters: 筛选实验的条件,可选
"""
return Experiments(self._client, path=path, login_info=self._login_info, filters=filters)

def run(
self,
path: str,
) -> Experiment:
"""
获取指定实验的信息
:param path: 实验路径,格式为 'username/project/run_id'
:return: Experiment 实例,包含实验信息
"""
# TODO: 待后端完善后替换成专用的接口
if len(path.split('/')) != 3:
raise ValueError(f"User's {path} is invaded. Correct path should be like 'username/project/run_id'")
_data = get_single_experiment(self._client, path=path)
proj_path = path.rsplit('/', 1)[0]
data = get_project_experiments(
self._client, path=proj_path, filters={'name': _data['name'], 'created_at': _data['createdAt']}
)
return Experiment(
self._client,
data=data[0],
path=proj_path,
web_host=self._web_host,
login_user=self._login_user,
line_count=1,
)

def workspaces(
self,
username: str = None,
):
"""
获取当前登录用户的工作空间迭代器
当username为其他用户时,可以作为visitor访问其工作空间
"""
if username is None:
username = self._login_user
return Workspaces(self._client, username=username)

def workspace(
self,
username: str = None,
):
"""
获取当前登录用户的工作空间
"""
if username is None:
username = self._login_user
data = get_workspace_info(self._client, path=username)
return Workspace(self._client, data=data)

from swanlab.api.main import OpenApi

__all__ = [
"OpenApi"
]
__all__ = ["Api", "OpenApi"]
10 changes: 10 additions & 0 deletions swanlab/api/deprecated/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
"""
@author: Zhou QiYang
@file: __init__.py
@time: 2025/12/30 20:54
@description: 旧版OpenApi,即将遗弃
"""

from .main import OpenApi

__all__ = ["OpenApi"]
6 changes: 3 additions & 3 deletions swanlab/api/base.py → swanlab/api/deprecated/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@

import requests

from swanlab.api.types import ApiResponse
from swanlab.core_python import auth, create_session
from swanlab.log.log import SwanLog
from .types import ApiResponse

_logger: Optional[SwanLog] = None

Expand Down Expand Up @@ -64,7 +64,7 @@ def __init__(self, login_info: auth.LoginInfo):
self.__login_info: auth.LoginInfo = login_info
self.__session: requests.Session = self.__init_session()
self.service: OpenApiService = OpenApiService(self)

@property
def session(self) -> requests.Session:
"""
Expand Down Expand Up @@ -131,7 +131,7 @@ def get_project_info(self, username: str, projname: str) -> ApiResponse[dict]:
获取项目详情
"""
return self.http.get(f"/project/{username}/{projname}", params={})

@staticmethod
def fetch_paginated_api(
api_func: Callable[..., ApiResponse], # 分页 API 请求函数
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"""
from typing import List

from swanlab.api.base import ApiBase, ApiHTTP
from swanlab.api.types import ApiResponse, Experiment, Pagination
from .base import ApiBase, ApiHTTP
from .types import ApiResponse, Experiment, Pagination

try:
from pandas import DataFrame
Expand Down
4 changes: 2 additions & 2 deletions swanlab/api/group.py → swanlab/api/deprecated/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
组织相关的开放API
"""

from swanlab.api.base import ApiBase, ApiHTTP
from swanlab.api.types import ApiResponse
from .base import ApiBase, ApiHTTP
from .types import ApiResponse


class GroupAPI(ApiBase):
Expand Down
13 changes: 7 additions & 6 deletions swanlab/api/main.py → swanlab/api/deprecated/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@
"""
from typing import Dict, List, Union

from swanlab.api.base import ApiHTTP, get_logger
from swanlab.api.experiment import ExperimentAPI
from swanlab.api.group import GroupAPI
from swanlab.api.project import ProjectAPI
from swanlab.api.types import ApiResponse, Experiment, Project
from swanlab.core_python import auth
from swanlab.error import KeyFileError
from swanlab.log.log import SwanLog
from swanlab.package import get_key
from .base import ApiHTTP, get_logger
from .experiment import ExperimentAPI
from .group import GroupAPI
from .project import ProjectAPI
from .types import ApiResponse, Experiment, Project

try:
from pandas import DataFrame
Expand All @@ -28,6 +28,7 @@
class OpenApi:
def __init__(self, api_key: str = "", log_level: str = "info"):
self.__logger: SwanLog = get_logger(log_level)
self.__logger.warning("OpenApi will be soon deprecated in swanlab 0.8.0. Please use swanlab.Api() instead.")

if api_key:
self.__logger.debug("Using API key", api_key)
Expand Down Expand Up @@ -210,7 +211,7 @@ def get_summary(
return self.experiment.get_summary(
exp_id=exp_id,
pro_id=project_cuid,
root_exp_id=exp.data.get("rootExpId", ""),
root_exp_id=exp.data.get("rootExpId", ""),
root_pro_id=exp.data.get("rootProId", "")
)

Expand Down
4 changes: 2 additions & 2 deletions swanlab/api/project.py → swanlab/api/deprecated/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
项目相关的开放API
"""

from swanlab.api.base import ApiBase, ApiHTTP
from swanlab.api.types import ApiResponse, Pagination, Project
from .base import ApiBase, ApiHTTP
from .types import ApiResponse, Pagination, Project


class ProjectAPI(ApiBase):
Expand Down
File renamed without changes.
Loading