> 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/prodvinutye/python-sdk.md).

# Руководство по 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-модели.

***

## Sync vs Async — когда использовать каждый

| Сценарий использования          | Клиент         | Почему                                   |
| ------------------------------- | -------------- | ---------------------------------------- |
| Простые скрипты, разовые задачи | `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
# Sync
with CloreAI() as client:
    wallets = client.wallets()

# Async
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,          # По крайней мере 64 ГБ системной ОЗУ
    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`       | Системная ОЗУ в ГБ (свойство, из `specs.ram`)                  |
| `price_usd`      | `float \| None`       | Цена по запросу в USD (свойство, из `price.usd.on_demand_usd`) |
| `spot_price_usd` | `float \| None`       | Спотовая цена в USD (свойство)                                 |
| `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 ноутбука
)

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 ноутбука                |
| `command`            | `str`   | —          | Команда запуска                       |
| `spot_price`         | `float` | —          | Спотовая ставка (bid)                 |
| `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="Job complete")

# Отменить все активные заказы
orders = client.my_orders()
for order in orders:
    client.cancel_order(order.id, issue="Cleanup")
    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"Цена on-demand: {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,        # Цена on-demand в BTC
    spot=0.00000113          # Спотовая цена в BTC
)
print("Настройки обновлены")
```

***

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

Спотовые заказы на 30–50% дешевле, но могут быть прерваны, если кто-то перебьёт вашу ставку.

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

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

### Шаблон повторных попыток с бэкоффом

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"Превышен лимит, повтор через {delay}с...")
                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. Асинхронное пакетное создание заказов

```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` вызовами. Даже в асинхронном режиме заказы распределяются во времени, чтобы соблюдать лимиты скорости.
{% 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"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())
```

***

## Следующие шаги

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


---

# 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/prodvinutye/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.
