Skip to content

Latest commit

 

History

History
185 lines (152 loc) · 9.44 KB

File metadata and controls

185 lines (152 loc) · 9.44 KB

WOOFi Python SDK 提取计划

Context

当前 woofi-cli 已有清晰的 5 层架构(api → services → schemas → utils → constants),核心业务逻辑与 CLI 表现层分离良好。目标是将核心层提取为独立的 woofi Python 包,供第三方应用集成 WOOFi,同时 CLI 改为依赖 SDK。

设计原则

  • SDK 占据最简短包名from woofi import WOOFiClient,不带 _sdk 后缀
  • 两层接口 — 底层 raw 方法返回贴近上游的 Pydantic models,高层 helpers 返回 normalized models;不用 raw=True 开关
  • SDK 不包含 CLI 契约 — envelope、exit codes、presenters 不进入 SDK;SDK 用 Python 异常暴露错误
  • Semver 管理 — SDK 公共 API 的方法名、参数名、返回模型字段一旦发布按 semver 管理
  • v1 范围 — 同步接口优先;quotebuild-txes 不在 v1 范围内

目标结构

woofi-cli/                              # repo root
├── packages/
│   ├── woofi/                          # SDK 包 (pip install woofi)
│   │   ├── pyproject.toml              # deps: httpx, pydantic
│   │   ├── src/woofi/
│   │   │   ├── __init__.py             # 导出 WOOFiClient + 所有公开模型 + 异常
│   │   │   ├── client.py              # WOOFiClient 门面类
│   │   │   ├── exceptions.py          # SDK 异常层级
│   │   │   ├── constants.py           # ← 从 woofi_cli 移入
│   │   │   ├── api/                   # ← 从 woofi_cli/api/ 移入(内部模块)
│   │   │   ├── services/             # ← 从 woofi_cli/services/ 移入(内部模块)
│   │   │   ├── schemas/              # ← 从 woofi_cli/schemas/ 移入(公开模型)
│   │   │   └── utils/                # ← 从 woofi_cli/utils/ 移入 (decimals, time)
│   │   └── tests/
│   │       ├── test_client.py         # WOOFiClient 集成测试
│   │       ├── test_services/         # ← 从 tests/test_services/ 移入
│   │       └── test_utils.py          # ← 从 tests/test_utils.py 移入
│   └── woofi-cli/                      # CLI 包 (pip install woofi-cli)
│       ├── pyproject.toml              # deps: woofi, click, rich
│       ├── src/woofi_cli/
│       │   ├── __init__.py
│       │   ├── main.py
│       │   ├── cli_context.py
│       │   ├── exit_codes.py
│       │   ├── commands/
│       │   ├── presenters/
│       │   └── schemas/common.py       # SuccessEnvelope/ErrorEnvelope (CLI 专用)
│       └── tests/
│           ├── conftest.py
│           └── test_commands/
├── pyproject.toml                      # workspace 根配置 (开发用)
├── CLAUDE.md
└── README.md

SDK 公开 API — 两层接口

底层 typed client(返回 raw models,贴近上游)

from woofi import WOOFiClient

with WOOFiClient(timeout=30) as client:
    raw_stats = client.get_stats(period="1d", network="arbitrum")           # -> StatResponseRaw
    raw_sources = client.get_source_stats(period="1w", network="bsc")      # -> SourceStatResponseRaw
    raw_yield = client.get_yield(network="arbitrum")                        # -> YieldResponseRaw
    raw_staking = client.get_staking()                                      # -> StakingResponseRaw

高层 helpers(返回 normalized models,含计算/过滤/排序)

with WOOFiClient(timeout=30) as client:
    # Stats
    series = client.get_stat_series(period="1d", network="arbitrum")       # -> list[StatBucket]
    summary = client.get_stat_summary(period="1d", network="arbitrum")     # -> StatSummary
    sources = client.list_sources(period="1w", network="bsc", limit=5)     # -> list[SourceStat]
    top = client.get_top_source(period="1d", network="arbitrum")           # -> SourceStat | None

    # Yield
    vaults = client.list_vaults(network="arbitrum", symbol="USDC")         # -> list[YieldVault]
    vault = client.get_vault(network="arbitrum", address="0x...")           # -> YieldVault | None
    ysummary = client.get_yield_summary(network="arbitrum")                # -> YieldSummary

    # Staking
    staking = client.get_staking_info()                                    # -> StakingInfo

接口原则:

  • SDK 参数保留当前 CLI 的命名,降低迁移和认知成本
  • SDK 返回 typed Pydantic model,不返回 CLI envelope
  • CLI 风格的 raw=True 开关不进入 SDK;通过显式区分 raw method (get_stats) 和 helper method (get_stat_series) 实现

SDK 异常层级

WOOFiError                  # 基础异常
├── WOOFiHTTPError          # 上游返回非 2xx (含 status_code, response_body)
├── WOOFiTimeoutError       # 请求超时
├── WOOFiPayloadError       # 上游响应 JSON 解析失败
└── WOOFiValidationError    # Pydantic 模型校验失败

CLI 负责把这些异常映射为现有 exit codes 和 stderr JSON envelope。

实施阶段

Phase 1: 创建 SDK 包结构 + 迁移核心代码

  1. 创建 packages/woofi/ 目录结构及 pyproject.toml
    • name: woofi, deps: httpx>=0.27, pydantic>=2.0
    • build backend: hatchling
  2. 将以下模块从 src/woofi_cli/ 移动packages/woofi/src/woofi/:
    • api/ (client.py, stats_api.py, source_stats_api.py, yield_api.py, staking_api.py)
    • services/ (stats_service.py, yield_service.py, staking_service.py)
    • schemas/ (stats.py, source_stats.py, yield_schema.py, staking.py) — 不含 common.py
    • utils/decimals.py, utils/time.py
    • constants.py
  3. 更新所有移动文件内部 import:woofi_cli.xxxwoofi.xxx
  4. 移动对应测试到 packages/woofi/tests/

Phase 2: 创建 WOOFiClient 门面 + 异常

  1. 创建 packages/woofi/src/woofi/exceptions.py — 四类异常 (HTTP/Timeout/Payload/Validation)
  2. 创建 packages/woofi/src/woofi/client.py:
    • __init__(self, timeout=30, base_url=API_BASE_URL) — lazy httpx.Client
    • __enter__ / __exit__ 上下文管理器
    • 底层方法: get_stats(), get_source_stats(), get_yield(), get_staking() — 调用 api 层,返回 raw models
    • 高层 helpers: get_stat_series(), get_stat_summary(), list_sources(), get_top_source(), list_vaults(), get_vault(), get_yield_summary(), get_staking_info() — 调用 api 层 + service 层,返回 normalized models
    • _call() 内部方法统一捕获 httpx/json/pydantic 异常 → 转为 WOOFiError 子类
  3. 为 service 返回值创建新的 Pydantic 模型:
    • StatSummary — 聚合统计汇总
    • YieldSummary — 收益汇总
    • 给 Normalized 模型添加简短别名导出 (StatBucket, SourceStat, YieldVault, StakingInfo)
  4. 创建 __init__.py 导出所有公开 API

Phase 3: 重构 CLI 依赖 SDK

  1. 创建 packages/woofi-cli/ 目录结构及 pyproject.toml
    • deps: woofi, click>=8.1, rich>=13.0
  2. 将 CLI 层代码移入: main.py, cli_context.py, exit_codes.py, commands/, presenters/, schemas/common.py
  3. 更新 CLI commands 的 import 指向 woofi
  4. 简化命令层错误处理: 统一捕获 WOOFiError 子类 → exit code + error envelope
  5. CLI 的 --raw 模式改为调用 SDK 底层 raw 方法
  6. 移动 CLI 测试到 packages/woofi-cli/tests/
  7. 更新根 pyproject.toml 为开发 workspace 配置

Phase 4: 测试 + 文档

  1. SDK 测试 (packages/woofi/tests/):
    • test_client.py: 用 respx mock HTTP,测试全部 raw 方法和 helper 方法、返回类型、异常映射
    • test_services/: 现有 service 测试(更新 import)
    • test_utils.py: 现有工具测试
  2. CLI 测试 (packages/woofi-cli/tests/):
    • 现有 command 测试更新 mock target 为 SDK 路径
    • 增加 CLI 适配测试: SDK 成功时保持 JSON envelope,SDK 抛错时保持 exit code + 错误输出
  3. 第三方接入验证:
    • 服务端 import SDK 并直接拿 typed result,不依赖 Click、presenter、CLI envelope
  4. 文档:
    • README 增加 "Python SDK quick start" 和 "CLI quick start" 两段
    • 提供最少可用示例: 同步调用、raw vs normalized、错误处理
  5. 确保两个包各自 pytest 全部通过

关键文件参考

当前路径 用途 目标
src/woofi_cli/api/client.py HTTP 工厂 SDK: 融入 WOOFiClient
src/woofi_cli/api/stats_api.py fetch_stat() SDK: api/
src/woofi_cli/services/stats_service.py 最大 service 模块 SDK: services/
src/woofi_cli/schemas/common.py Envelope 模型 CLI 保留
src/woofi_cli/commands/stats.py 最大 command 模块 CLI: commands/
src/woofi_cli/constants.py URL/网络/周期常量 SDK: constants.py
src/woofi_cli/utils/errors.py WoofiCLIError 替换为 SDK exceptions

验证

  1. cd packages/woofi && pytest — 全部通过
  2. cd packages/woofi-cli && pytest — 全部通过
  3. pip install -e packages/woofi && python -c "from woofi import WOOFiClient; print('OK')" — 成功
  4. pip install -e packages/woofi-cli && woofi stats series --period 1d --network arbitrum --format json — 输出正常
  5. mypy packages/woofi/src — 无错误
  6. 第三方场景验证 — 纯 SDK import 不触发任何 click/rich 依赖

Scope 边界

  • v1 只做同步接口;如有高并发需求再补 AsyncWOOFiClient
  • quotebuild-txes 不在 SDK v1 范围内