> For the complete documentation index, see [llms.txt](https://docs.clore.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.clore.ai/guides/guides_v2-ru/prodvinutyi/python-sdk.md).

# Руководство по Python SDK

Полное руководство по Python SDK — синхронные/асинхронные клиенты, фильтрация на маркетплейсе, жизненный цикл ордеров, спотовый рынок, операции с кошельком и обработка ошибок

{% hint style="success" %}
**Новичок в SDK?** Начните с [5-минутного быстрого старта](/guides/guides_v2-ru/nachalo-raboty/python-quickstart.md) сначала.
{% endhint %}

Для краткого руководства с реальными примерами см. [Clore.ai Python SDK — автоматизируйте свои GPU-рабочие процессы за 5 минут](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` | Создание нескольких заказов параллельно      |
| Веб-приложения                  | `AsyncCloreAI` | Неблокирующий ввод-вывод                     |

### Пример синхронного использования

```python
from clore_ai import CloreAI

client = CloreAI()  # Использует переменную окружения CLORE_API_KEY

servers = client.marketplace(gpu="RTX 4090")
print(f"Найдено {len(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"Найдено {len(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):** Автоматический экспоненциальный backoff

***

## Фильтрация маркетплейса

Параметр `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,          # Не менее 64 ГБ системной RAM
    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[0].id} — ${cheapest[0].price_usd:.4f}/ч")
```

### Поля модели сервера

Каждый `объект MarketplaceServer` имеет следующие атрибуты и удобные свойства:

| Поле             | Тип                   | Описание                                                          |
| ---------------- | --------------------- | ----------------------------------------------------------------- |
| `id`             | `int`                 | ID сервера (используйте это в `create_order`)                     |
| `gpu_model`      | `str \| None`         | Описание GPU из спецификаций (свойство)                           |
| `gpu_count`      | `int`                 | Количество GPU из `gpu_array` (свойство)                          |
| `ram_gb`         | `float \| None`       | Системная RAM в ГБ (свойство, из `specs.ram`)                     |
| `price_usd`      | `float \| None`       | Цена по требованию в USD (свойство, из `price.usd.on_demand_usd`) |
| `spot_price_usd` | `float \| None`       | Спотовая цена в USD (свойство)                                    |
| `доступные`      | `bool`                | Находится ли сервер не в аренде (свойство)                        |
| `location`       | `str \| None`         | Код страны из сетевых спецификаций (свойство)                     |
| `specs`          | `ServerSpecs \| None` | Аппаратные характеристики (cpu, ram, disk, gpu, net)              |
| `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"ID заказа: {order.id}")
print(f"IP: {order.pub_cluster}")
print(f"Порты: {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`   | —         | Точка входа Auto SSH                   |

### Список заказов

```python
# Только активные заказы
active = client.my_orders()
for o in active:
    print(f"Заказ {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_id} не готов через {timeout}с")

# Использование
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="Задача завершена")

# Отменить все активные заказы
orders = client.my_orders()
for order in orders:
    client.cancel_order(order.id, issue="Очистка")
    print(f"Заказ {order.id} отменён")
```

***

## Управление сервером (для хостеров)

Если вы размещаете GPU на Clore, SDK позволяет управлять вашими серверами:

### Список ваших серверов

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

### Получить конфигурацию сервера

```python
config = client.server_config("MyGPU-Rig")
print(f"Имя: {config.name}")
print(f"Видимость: {config.visibility}")
print(f"Онлайн: {config.online}")
print(f"Мин. аренда: {config.mrl}ч")
print(f"Цена по требованию: {config.on_demand_price}")
print(f"Спотовая цена: {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("Настройки обновлены")
```

***

## Спотовый рынок

Спотовые заказы могут быть прерваны, если кто-то перебьёт вашу ставку. Примерно у трети серверов спотовая цена ниже цены по требованию (медиана \~13% ниже); у остальных спотовая цена равна цене по требованию.

### Просмотреть спотовые предложения

```python
offers = client.spot_marketplace(server_id=6)
for offer in offers:
    print(f"Заказ {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"Спотовый заказ {order.id} создан")
```

### Изменить спотовую цену

```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("Нет спотовых предложений — используйте цену по требованию как базовую")
        return None

    min_price = min(o["price"] for o in offers)
    bid = min_price * (1 + premium_pct / 100)
    print(f"Мин. цена рынка: {min_price}, ставка: {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"  Адрес для пополнения: {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"⚠️  Низкий баланс BTC: {w.balance:.8f} (минимум: {min_btc})")
            return False

    print("✅ Балансы в порядке")
    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("Неверный API-ключ — проверьте CLORE_API_KEY")
except InvalidInputError as e:
    print(f"Некорректный ввод: {e}")
except RateLimitError:
    print("Ограничение скорости — SDK пытается повторно автоматически, но число повторов исчерпано")
except CloreAPIError as e:
    print(f"Ошибка API (код {e.code}): {e}")
```

### Паттерн повторных попыток с backoff

В 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):
    """Повторяет операцию API Clore с экспоненциальной задержкой."""
    for attempt in range(max_attempts):
        try:
            return func()
        except RateLimitError:
            if attempt < max_attempts - 1:
                delay = base_delay * (2 ** attempt)
                print(f"Превышен лимит частоты, повтор через {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. Используйте async для одновременных операций

```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)} серверов")
        print(f"RTX 3090: {len(rtx3090)} серверов")
        print(f"A100: {len(a100)} серверов")

asyncio.run(compare_gpus())
```

### 3. Создание пакетных заказов в async

```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"Сервер {sid}: ОШИБКА — {result}")
            else:
                print(f"Сервер {sid}: Заказ {result.id} создан")

        return orders

# Разверните на 3 серверах одновременно
asyncio.run(batch_deploy([142, 305, 891]))
```

{% hint style="warning" %}
**Примечание:** SDK требует 5-секундную паузу между `create_order` вызовами. Даже в async-режиме заказы распределяются по времени, чтобы соблюдать ограничения по частоте.
{% 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_count}/{target_workers}")

        if active_count >= target_workers:
            print("Уже достигнут целевой уровень. Делать ничего не нужно.")
            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"Доступно только {len(candidates)} серверов (нужно {needed})")

        # 3. Развертывание
        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"Развернуто на сервере {server.id} → заказ {order.id}")
            except CloreAPIError as e:
                print(f"Не удалось развернуть на {server.id}: {e}")

asyncio.run(auto_scale())
```

***

## Дальнейшие шаги

* [Автоматизация CLI](/guides/guides_v2-ru/prodvinutyi/cli-automation.md) — Bash-скрипты, CI/CD, пакетные операции
* [Пакетная обработка](/guides/guides_v2-ru/prodvinutyi/batch-processing.md) — Обрабатывайте большие нагрузки на GPU Clore
* [Интеграция API](/guides/guides_v2-ru/prodvinutyi/api-integration.md) — Подключайте ИИ-сервисы к своим приложениям


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.clore.ai/guides/guides_v2-ru/prodvinutyi/python-sdk.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
