# Python SDK 指南

{% hint style="success" %}
**第一次使用 SDK？** 从 [5 分钟快速入门](https://docs.clore.ai/guides/guides_v2-zh/ru-men/python-quickstart) 开始。
{% endhint %}

有关带有真实示例的快速入门教程，请参见 [Clore.ai Python SDK — 在 5 分钟内自动化您的 GPU 工作流程](https://blog.clore.ai/cloreai-python-sdk-automate-your-gpu-workflows-in-5-minutes/)

## 安装

```bash
pip install clore-ai
```

该 SDK 提供两个客户端：

* **`CloreAI`** — 同步（更简单，适合脚本）
* **`AsyncCloreAI`** — 异步（并发操作更快）

两者共享相同的方法并返回相同的 Pydantic 模型。

***

## 同步 vs 异步 — 何时使用各自方式

| 使用场景       | 客户端            | 原因                     |
| ---------- | -------------- | ---------------------- |
| 简单脚本，一次性任务 | `CloreAI`      | 代码更简单，无需 `async/await` |
| 监控循环       | `CloreAI`      | 顺序检查即可                 |
| 批量市场查询     | `AsyncCloreAI` | 并发请求 = 更快              |
| 批量订单创建     | `AsyncCloreAI` | 并行创建多个订单               |
| Web 应用     | `AsyncCloreAI` | 非阻塞 I/O                |

### 同步示例

```python
from clore_ai import CloreAI

client = CloreAI()  # 使用 CLORE_API_KEY 环境变量

servers = client.marketplace(gpu="RTX 4090")
print(f"Found {len(servers)} servers")

client.close()  # 或使用上下文管理器
```

### 异步示例

```python
import asyncio
from clore_ai import AsyncCloreAI

async def main():
    async with AsyncCloreAI() as client:
        servers = await client.marketplace(gpu="RTX 4090")
        print(f"Found {len(servers)} servers")

asyncio.run(main())
```

### 上下文管理器（推荐）

两个客户端都支持上下文管理器以便自动清理：

```python
# 同步
with CloreAI() as client:
    wallets = client.wallets()

# 异步
async with AsyncCloreAI() as client:
    wallets = await client.wallets()
```

***

## 客户端配置

```python
client = CloreAI(
    api_key="your_key",      # 或设置 CLORE_API_KEY 环境变量
    base_url="https://api.clore.ai/v1",  # 自定义 API 端点
    timeout=30.0,            # 请求超时（秒）
    max_retries=3            # 在速率限制/网络错误时重试次数
)
```

SDK 包含内置的速率限制器：

* **常规请求：** 1 次请求/秒
* **`create_order`:** 调用之间 5 秒冷却时间
* **速率限制错误（代码 5）：** 自动指数退避

***

## 市场过滤

该 `marketplace()` 方法获取所有可用服务器并在客户端进行过滤：

```python
from clore_ai import CloreAI

client = CloreAI()

# 所有可用服务器
all_servers = client.marketplace()

# 按 GPU 型号过滤（大小写不敏感的子字符串匹配）
rtx_4090s = client.marketplace(gpu="RTX 4090")

# 按多个条件过滤
budget_gpus = client.marketplace(
    gpu="RTX 4090",
    max_price_usd=1.0,       # 最高 $1.00/小时
    min_gpu_count=2,          # 至少 2 块 GPU
    min_ram_gb=64.0,          # 至少 64GB 系统内存
    available_only=True       # 仅可用服务器（默认）
)
```

### 高级过滤（客户端侧）

对于方法未内置的过滤器，请对返回的 `Server` 对象自行过滤：

```python
servers = client.marketplace(gpu="RTX 4090")

# 位于欧盟且可靠性高的服务器
eu_servers = [
    s for s in servers
    if s.location and s.location.upper() in ("DE", "FR", "NL", "FI")
    and s.reliability and s.reliability >= 0.95
]

# 按价格排序
cheapest = sorted(servers, key=lambda s: s.price_usd or float("inf"))
print(f"Cheapest: Server {cheapest[0].id} — ${cheapest[0].price_usd:.4f}/h")
```

### 服务器模型字段

每个 `MarketplaceServer` 对象具有这些属性和便捷属性：

| 字段               | 类型                    | 描述                                        |
| ---------------- | --------------------- | ----------------------------------------- |
| `id`             | `int`                 | 服务器 ID（在 `create_order`)                  |
| `gpu_model`      | `str \| None`         | 来自规格的 GPU 描述（属性）                          |
| `gpu_count`      | `int`                 | GPU 的数量，来自 `gpu_array` （属性）               |
| `ram_gb`         | `float \| None`       | 系统内存（GB）（属性，来自 `specs.ram`)               |
| `price_usd`      | `float \| None`       | 按需价格（美元）（属性，来自 `price.usd.on_demand_usd`) |
| `spot_price_usd` | `float \| None`       | 现货价格（美元）（属性）                              |
| `available`      | `bool`                | 服务器是否未被租用（属性）                             |
| `location`       | `str \| None`         | 来自网络规格的国家代码（属性）                           |
| `specs`          | `ServerSpecs \| None` | 硬件规格（cpu、内存、磁盘、GPU、网络）                    |
| `price`          | `ServerPrice \| None` | 完整定价结构                                    |
| `rented`         | `bool \| None`        | 服务器当前是否被租用                                |

***

## 订单管理

### 创建订单

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="on-demand",               # "on-demand" 或 "spot"
    currency="bitcoin",             # 支付货币
    ssh_password="MySecurePass",    # SSH 访问
    ports={"22": "tcp", "8888": "http"},  # 端口映射
    env={"HF_TOKEN": "hf_xxx"},    # 环境变量
    command="bash /start.sh",       # 自定义启动命令
    jupyter_token="my_token"        # Jupyter notebook 令牌
)

print(f"Order ID: {order.id}")
print(f"IP: {order.pub_cluster}")
print(f"Ports: {order.tcp_ports}")
```

### 完整 `create_order` 参数

| 参数                   | 类型      | 必需 | 描述                       |
| -------------------- | ------- | -- | ------------------------ |
| `server_id`          | `int`   | ✅  | 要租用的服务器                  |
| `image`              | `str`   | ✅  | Docker 镜像                |
| `type`               | `str`   | ✅  | `"on-demand"` 或 `"spot"` |
| `currency`           | `str`   | ✅  | 支付货币（例如， `"bitcoin"`)    |
| `ssh_password`       | `str`   | —  | SSH 密码                   |
| `ssh_key`            | `str`   | —  | SSH 公钥                   |
| `ports`              | `dict`  | —  | 端口映射（`{"22": "tcp"}`)    |
| `env`                | `dict`  | —  | 环境变量                     |
| `jupyter_token`      | `str`   | —  | Jupyter notebook 令牌      |
| `command`            | `str`   | —  | 启动命令                     |
| `spot_price`         | `float` | —  | 现货出价价格                   |
| `required_price`     | `float` | —  | 所需价格                     |
| `autossh_entrypoint` | `str`   | —  | 自动 SSH 入口点               |

### 列出订单

```python
# 仅活动订单
active = client.my_orders()
for o in active:
    print(f"Order {o.id}: type={o.type}, IP={o.pub_cluster}, status={o.status}")

# 包括已完成订单
all_orders = client.my_orders(include_completed=True)
```

### 订单模型字段

| 字段            | 类型              | 描述                        |
| ------------- | --------------- | ------------------------- |
| `id`          | `int`           | 订单 ID                     |
| `server_id`   | `int \| None`   | 被租用的服务器 ID                |
| `type`        | `str`           | `"on-demand"` 或 `"spot"`  |
| `status`      | `str \| None`   | 订单状态                      |
| `image`       | `str \| None`   | Docker 镜像                 |
| `currency`    | `str \| None`   | 支付货币                      |
| `price`       | `float \| None` | 价格                        |
| `pub_cluster` | `str \| None`   | 公网 IP / 主机名               |
| `tcp_ports`   | `dict \| None`  | 端口映射（例如， `{"22": 50022}`) |
| `created_at`  | `str \| None`   | 创建时间戳                     |

### 监控订单

```python
import time

def wait_for_ready(client, order_id, timeout=120):
    """等待订单获得公网 IP。"""
    for _ in range(timeout // 10):
        orders = client.my_orders()
        order = next((o for o in orders if o.id == order_id), None)
        if order and order.pub_cluster:
            return order
        time.sleep(10)
    raise TimeoutError(f"Order {order_id} not ready after {timeout}s")

# 用法
order = client.create_order(server_id=142, image="cloreai/ubuntu22.04-cuda12", type="on-demand", currency="bitcoin")
ready = wait_for_ready(client, order.id)
print(f"SSH: ssh root@{ready.pub_cluster} -p {ready.tcp_ports.get('22', 22)}")
```

### 取消订单

```python
# 带可选原因取消
client.cancel_order(order_id=38, issue="Job complete")

# 取消所有活动订单
orders = client.my_orders()
for order in orders:
    client.cancel_order(order.id, issue="Cleanup")
    print(f"Cancelled order {order.id}")
```

***

## 服务器管理（针对主机方）

如果您在 Clore 上托管 GPU，SDK 允许您管理您的服务器：

### 列出您的服务器

```python
my_servers = client.my_servers()
for s in my_servers:
    print(f"Server {s.id}: {s.gpu_model} — {s.status}")
```

### 获取服务器配置

```python
config = client.server_config("MyGPU-Rig")
print(f"Name: {config.name}")
print(f"Visibility: {config.visibility}")
print(f"Online: {config.online}")
print(f"Min rental: {config.mrl}h")
print(f"On-demand price: {config.on_demand_price}")
print(f"Spot price: {config.spot_price}")
```

### 更新服务器设置

```python
client.set_server_settings(
    name="MyGPU-Rig",
    availability=True,       # 使服务器可用
    mrl=24,                  # 最低 24 小时租期
    on_demand=0.0001,        # 按需价格（BTC）
    spot=0.00000113          # 现货价格（BTC）
)
print("Settings updated")
```

***

## 现货市场

现货订单便宜 30–50%，但如果有人出价更高，可能会被中断。

### 查看现货报价

```python
offers = client.spot_marketplace(server_id=6)
for offer in offers:
    print(f"Order {offer.get('order_id')}: price={offer.get('price')}")
```

### 创建现货订单

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="spot",
    currency="bitcoin",
    spot_price=0.0001,       # 您的出价
    ssh_password="MyPass"
)
print(f"Spot order {order.id} created")
```

### 调整现货价格

```python
# 提高您的出价以避免被超出
client.set_spot_price(order_id=39, price=0.000003)
```

### 现货竞价策略

```python
from clore_ai import CloreAI

client = CloreAI()

def smart_spot_bid(server_id, premium_pct=5):
    """以略高于当前最低现货价的价格出价。"""
    offers = client.spot_marketplace(server_id=server_id)
    if not offers:
        print("No spot offers — use on-demand price as baseline")
        return None

    min_price = min(o["price"] for o in offers)
    bid = min_price * (1 + premium_pct / 100)
    print(f"Market min: {min_price}, bidding: {bid:.8f} (+{premium_pct}%)")
    return bid

# 用法
bid = smart_spot_bid(server_id=142, premium_pct=10)
if bid:
    order = client.create_order(
        server_id=142,
        image="cloreai/ubuntu22.04-cuda12",
        type="spot",
        currency="bitcoin",
        spot_price=bid
    )
```

***

## 钱包操作

### 检查余额

```python
wallets = client.wallets()
for w in wallets:
    print(f"{w.name}: {w.balance:.8f}")
    if w.deposit:
        print(f"  Deposit address: {w.deposit}")
```

### 低余额提醒

```python
from clore_ai import CloreAI

def check_balance(min_btc=0.001):
    """如果 BTC 余额低于阈值则发出提醒。"""
    client = CloreAI()
    wallets = client.wallets()

    for w in wallets:
        if w.name.lower() == "bitcoin" and w.balance < min_btc:
            print(f"⚠️  Low BTC balance: {w.balance:.8f} (minimum: {min_btc})")
            return False

    print("✅ Balances OK")
    return True

check_balance(min_btc=0.001)
```

***

## 错误处理最佳实践

### 异常层次结构

```
CloreAPIError（基类）
├── DBError           (代码 1) — 数据库错误
├── InvalidInputError (代码 2) — 错误的输入
├── AuthError         (代码 3) — 无效的 API 密钥
├── InvalidEndpointError (代码 4) — 错误的端点
├── RateLimitError    (代码 5) — 被限流（自动重试）
└── FieldError        (代码 6) — 字段特定错误
```

### 基础错误处理

```python
from clore_ai import CloreAI
from clore_ai.exceptions import (
    CloreAPIError,
    AuthError,
    RateLimitError,
    InvalidInputError
)

client = CloreAI()

try:
    order = client.create_order(
        server_id=999999,
        image="cloreai/ubuntu22.04-cuda12",
        type="on-demand",
        currency="bitcoin"
    )
except AuthError:
    print("Invalid API key — check CLORE_API_KEY")
except InvalidInputError as e:
    print(f"Bad input: {e}")
except RateLimitError:
    print("Rate limited — the SDK retries automatically, but max retries exceeded")
except CloreAPIError as e:
    print(f"API error (code {e.code}): {e}")
```

### 带退避的重试模式

SDK 对速率限制和网络错误具有内置重试（`max_retries=3`）。对于应用层的重试：

```python
import time
from clore_ai import CloreAI
from clore_ai.exceptions import CloreAPIError, RateLimitError

def retry_operation(func, max_attempts=3, base_delay=2.0):
    """使用指数退避重试 Clore API 操作。"""
    for attempt in range(max_attempts):
        try:
            return func()
        except RateLimitError:
            if attempt < max_attempts - 1:
                delay = base_delay * (2 ** attempt)
                print(f"Rate limited, retrying in {delay}s...")
                time.sleep(delay)
            else:
                raise
        except CloreAPIError as e:
            if e.code in (1,):  # 数据库错误可能是暂时性的
                if attempt < max_attempts - 1:
                    time.sleep(base_delay)
                    continue
            raise

# 用法
client = CloreAI()
servers = retry_operation(lambda: client.marketplace(gpu="RTX 4090"))
```

***

## 性能提示

### 1. 重用客户端

```python
# ❌ 错误 — 每次都会创建新的 HTTP 连接
for _ in range(10):
    client = CloreAI()
    client.marketplace()
    client.close()

# ✅ 正确 — 重用 HTTP 连接
client = CloreAI()
for _ in range(10):
    client.marketplace()
client.close()
```

### 2. 对于并发操作使用异步

```python
import asyncio
from clore_ai import AsyncCloreAI

async def compare_gpus():
    async with AsyncCloreAI() as client:
        # 并行运行 3 个搜索
        rtx4090, rtx3090, a100 = await asyncio.gather(
            client.marketplace(gpu="RTX 4090"),
            client.marketplace(gpu="RTX 3090"),
            client.marketplace(gpu="A100"),
        )

        print(f"RTX 4090: {len(rtx4090)} servers")
        print(f"RTX 3090: {len(rtx3090)} servers")
        print(f"A100: {len(a100)} servers")

asyncio.run(compare_gpus())
```

### 3. 异步批量创建订单

```python
import asyncio
from clore_ai import AsyncCloreAI

async def batch_deploy(server_ids):
    async with AsyncCloreAI() as client:
        tasks = [
            client.create_order(
                server_id=sid,
                image="cloreai/ubuntu22.04-cuda12",
                type="on-demand",
                currency="bitcoin",
                ssh_password="BatchPass123",
                ports={"22": "tcp"}
            )
            for sid in server_ids
        ]
        orders = await asyncio.gather(*tasks, return_exceptions=True)

        for sid, result in zip(server_ids, orders):
            if isinstance(result, Exception):
                print(f"Server {sid}: FAILED — {result}")
            else:
                print(f"Server {sid}: Order {result.id} created")

        return orders

# 同时在 3 台服务器上部署
asyncio.run(batch_deploy([142, 305, 891]))
```

{% hint style="warning" %}
**注意：** SDK 在调用之间强制执行 5 秒的冷却时间。 `create_order` 即使在异步模式下，订单也会被间隔开以遵守速率限制。
{% endhint %}

### 4. 完成后关闭客户端

```python
# 上下文管理器会自动处理此项
with CloreAI() as client:
    # 工作...
    pass  # client.close() 会被自动调用

# 或手动关闭
client = CloreAI()
try:
    # 工作...
    pass
finally:
    client.close()
```

***

## 完整示例：自动扩缩 GPU 工作节点

```python
import asyncio
import time
from clore_ai import AsyncCloreAI
from clore_ai.exceptions import CloreAPIError

async def auto_scale(
    gpu_model="RTX 4090",
    max_price=2.0,
    target_workers=3,
    image="cloreai/ubuntu22.04-cuda12"
):
    """维持一个 GPU 工作节点池。"""
    async with AsyncCloreAI() as client:
        # 1. 检查当前订单
        current_orders = await client.my_orders()
        active_count = len(current_orders)
        print(f"Active workers: {active_count}/{target_workers}")

        if active_count >= target_workers:
            print("Already at target. Nothing to do.")
            return

        # 2. 查找可用服务器
        servers = await client.marketplace(gpu=gpu_model, max_price_usd=max_price)
        servers.sort(key=lambda s: s.price_usd or float("inf"))

        needed = target_workers - active_count
        candidates = servers[:needed]

        if len(candidates) < needed:
            print(f"Only {len(candidates)} servers available (need {needed})")

        # 3. Deploy
        for server in candidates:
            try:
                order = await client.create_order(
                    server_id=server.id,
                    image=image,
                    type="on-demand",
                    currency="bitcoin",
                    ssh_password="WorkerPass123",
                    ports={"22": "tcp"}
                )
                print(f"Deployed on server {server.id} → order {order.id}")
            except CloreAPIError as e:
                print(f"Failed to deploy on {server.id}: {e}")

asyncio.run(auto_scale())
```

***

## 下一步

* [命令行自动化](https://docs.clore.ai/guides/guides_v2-zh/gao-ji/cli-automation) — Bash 脚本、CI/CD、批处理操作
* [批量处理](https://docs.clore.ai/guides/guides_v2-zh/gao-ji/batch-processing) — 在 Clore GPU 上处理大规模工作负载
* [API 集成](https://docs.clore.ai/guides/guides_v2-zh/gao-ji/api-integration) — 将 AI 服务连接到您的应用


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-zh/gao-ji/python-sdk.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
