一个 async with 管全生命周期:进入即认证、退出即释放、请求自动续期。


安装

pip install gausium-sdk

# 如需运行 Demo
pip install gausium-sdk[demo]

基本用法

from gausium_sdk import GausiumSDK

async with GausiumSDK(
    client_id="your_client_id",
    client_secret="your_client_secret",
    open_access_key="your_open_access_key",
) as sdk:
    # 获取机器人列表
    robots = await sdk.robot.list()

    # 查询机器人状态
    status = await sdk.robot.get_status("SN123")

    # 发送指令
    await sdk.command.send("SN123", command_type="pause")

GausiumSDK 类

GausiumSDK 是 SDK 的唯一入口。构造函数接收三个必填参数:client_id(应用 ID)、client_secret(应用密钥)、open_access_key(开放平台访问密钥)。内部创建 AuthClient 实例,并通过 Python property 懒加载五个业务服务。

构造函数

class GausiumSDK:
    def __init__(self, client_id: str, client_secret: str, open_access_key: str):
        self.auth = Auth(client_id, client_secret, open_access_key)
        self.client = Client(self.auth)

服务属性(懒加载)

属性类型说明
sdk.robotRobotService机器人信息服务(6 个接口)
sdk.taskTaskService任务服务(3 个接口)
sdk.commandCommandService指令服务(4 个接口)
sdk.mapMapService地图服务(5 个接口)
sdk.cleaningCleaningService清洁数据服务(4 个接口)
口诀RTCMC -- 机器人、任务、指令、地图、清洁

每个属性首次访问时才 import 对应的 Service 类并实例化,传入 Client 实例。后续访问直接返回缓存的实例。

上下文管理器

方法行为
__aenter__()调用 Client.__aenter__() 创建 httpx 连接池,然后调用 Auth.login() 获取初始 token
__aexit__(*args)调用 Client.__aexit__() 关闭 httpx 连接池
口诀进入 = 建池 + 登录,退出 = 关池

Auth 认证模块

Auth 类管理 OAuth2 全生命周期。使用自定义 grant_type urn:gaussian:params:oauth:grant-type:open-access-tokenPOST /gas/api/v1alpha1/oauth/token 获取初始 token,之后通过 refresh_token grant_type 自动续期。内部使用 asyncio.Lock 防止并发刷新。

TokenData 模型

class TokenData(BaseModel):
    token_type: str
    access_token: str
    expires_in: int          # API 返回的存活秒数
    refresh_token: str
    obtained_at: float       # 获取时刻(time.time())

    @property
    def expires_at(self) -> float:
        """过期时刻 = obtained_at + expires_in"""
        return self.obtained_at + self.expires_in

Auth 常量与属性

名称类型值 / 说明
BASE_URL模块常量https://openapi.gs-robot.com
TOKEN_ENDPOINT模块常量/gas/api/v1alpha1/oauth/token
GRANT_TYPE模块常量urn:gaussian:params:oauth:grant-type:open-access-token
REFRESH_BUFFER类常量300(提前 300 秒 / 5 分钟续期)
access_tokenproperty返回当前 token 字符串,无 token 时返回 None
is_expiredproperty判断 token 是否已过期或即将过期(含 REFRESH_BUFFER)
口诀BASE + ENDPOINT + GRANT + BUFFER + 两个 property

Auth 方法

方法签名说明
loginasync def login(self, client: httpx.AsyncClient) -> TokenData使用 client_id / client_secret / open_access_key 获取初始 token
refreshasync def refresh(self, client: httpx.AsyncClient) -> TokenData使用 refresh_token 续期,内部加 asyncio.Lock 并做双重检查
ensure_validasync def ensure_valid(self, client: httpx.AsyncClient) -> None检查 is_expired,自动决定 login 还是 refresh
口诀login 首次、refresh 续期、ensure_valid 自动判断

Client HTTP 客户端

Client 封装 httpx.AsyncClient,提供 getpost 两个方法。每次请求前自动调用 Auth.ensure_valid() 确保 token 有效,并通过 _headers() 注入 Authorization: Bearer <token>Content-Type: application/json 请求头。

构造函数

class Client:
    def __init__(
        self,
        auth: Auth,
        base_url: str = "https://openapi.gs-robot.com",
        timeout: float = 30.0,
    ):
        self.auth = auth
        self.base_url = base_url
        self.timeout = timeout

连接池配置

参数说明
base_urlhttps://openapi.gs-robot.comAPI 基础地址
timeout30.0单次请求超时
max_connections10连接池最大连接数
max_keepalive_connections5最大保活连接数
口诀30 秒超时、10 总连接、5 保活

Client 方法

方法签名说明
getasync def get(self, path: str, params: Optional[Dict[str, Any]] = None) -> dict发送 GET 请求,自动注入 Bearer token,返回 JSON 响应
postasync def post(self, path: str, json: Optional[Dict[str, Any]] = None) -> dict发送 POST 请求(JSON body),自动注入 Bearer token,返回 JSON 响应
_ensure_tokenasync def _ensure_token(self) -> None内部方法,在每次请求前调用 Auth.ensure_valid()
_headersdef _headers(self) -> Dict[str, str]生成请求头(Authorization + Content-Type),未认证时抛出 ValueError
口诀get / post 对外,_ensure_token / _headers 对内

请求流程

  1. 调用 get()post()
  2. 内部先执行 _ensure_token() -> Auth.ensure_valid() -> 按需 login 或 refresh
  3. 通过 _headers() 构造 Authorization: Bearer <token> 请求头
  4. 使用 httpx.AsyncClient 发送请求
  5. 调用 res.raise_for_status() 检查 HTTP 状态码
  6. 返回 res.json() 解析后的字典

服务层

服务层按业务域划分为五个独立模块,每个服务接收 Client 实例并通过它发送 HTTP 请求。所有服务通过 GausiumSDK 的懒加载属性访问,首次访问时才 import 和实例化。

# 服务通过属性访问,首次使用时自动加载
async with GausiumSDK(client_id, client_secret, open_access_key) as sdk:
    # 访问 sdk.robot 时才 import RobotService
    robots = await sdk.robot.list()

    # 访问 sdk.cleaning 时才 import CleaningService
    reports = await sdk.cleaning.get_reports("SN123")

最佳实践

推荐做法
  • 始终使用 async with GausiumSDK(...) as sdk: 上下文管理器
  • ensure_valid() 自动处理 token 续期,无需手动管理
  • 复用同一个 SDK 实例发起所有 API 调用,充分利用连接池
  • 在服务端常驻进程中保持 SDK 实例存活以复用 TCP 连接
不推荐
  • 手动调用 Auth.login()Auth.refresh() 管理 token
  • 创建多个 GausiumSDK 实例连接同一个应用
  • 不使用 async with 直接调用 sdk.client.get() -- 连接池未初始化会抛 RuntimeError
  • 在每次请求后关闭再重建 SDK 实例 -- 浪费连接池和 token
常见误区
  • 忘记 async with 导致 RuntimeError: Client not started
  • __aenter__ 之前访问服务属性,此时 Client._httpNone
  • token 过期后手动 login 而非依赖 ensure_valid,可能导致并发竞态

一个 SDK 实例管理一个应用的全部 API 调用


架构总览

Auth 管 token 生命周期,Client 管 HTTP 请求和连接池,SDK 管服务编排和资源释放。三层各司其职,通过 async with 一键串联。

— GausiumSDK 设计原则