# SDK Python (clore-ai)

Le **clore-ai** le package est le SDK Python officiel pour le [Clore.ai](https://clore.ai) place de marché GPU. Il enveloppe l’ensemble de l’API REST dans une interface propre et typée avec limitation de débit intégrée, tentatives automatiques et gestion structurée des erreurs — afin que vous puissiez vous concentrer sur la location de GPU, pas sur la plomberie HTTP.

***

## Installation

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

**Exigences :** Python 3.9+

Le package installe à la fois le SDK Python et le [`clore` CLI](/clore.ai/clore.ai-eng-fr/developpeurs/cli-guide.md).

***

## Authentification

Obtenez votre clé API depuis le [tableau de bord Clore.ai](https://clore.ai) → **API** section.

### Option 1 : variable d’environnement (recommandée)

```bash
export CLORE_API_KEY=your_api_key_here
```

Le SDK lit `CLORE_API_KEY` automatiquement — aucun changement de code requis.

### Option 2 : fichier de config CLI

```bash
clore config set api_key YOUR_API_KEY
```

Cela stocke la clé dans `~/.clore/config.json`.

### Option 3 : passer directement dans le code

```python
from clore_ai import CloreAI

client = CloreAI(api_key="your_api_key_here")
```

> ⚠️ **Important :** L’API Clore.ai utilise l’ `entête auth` pour l’authentification, **et non** `Authorization: Bearer`. Le SDK gère cela automatiquement.

***

## Démarrage rapide

```python
from clore_ai import CloreAI

client = CloreAI()
servers = client.marketplace(gpu="RTX 4090", max_price_usd=5.0)
for s in servers:
    print(f"Server {s.id}: {s.gpu_model} — ${s.price_usd:.4f}/h")
```

***

## Client synchrone (`CloreAI`)

### Constructeur

```python
CloreAI(
    api_key: str | None = None,       # Repli sur la variable d’environnement / config CLORE_API_KEY
    base_url: str | None = None,       # Par défaut : https://api.clore.ai/v1
    timeout: float = 30.0,             # Délai d’attente de la requête en secondes
    max_retries: int = 3               # Tentatives de nouvelle tentative en cas de limite de débit / erreurs réseau
)
```

Le client prend en charge les gestionnaires de contexte pour un nettoyage automatique :

```python
with CloreAI() as client:
    wallets = client.wallets()
    # client.close() appelé automatiquement
```

***

### `wallets()`

Obtenez vos soldes de portefeuille et adresses de dépôt.

```python
wallets = client.wallets()

for wallet in wallets:
    print(f"{wallet.name}: {wallet.balance:.8f}")
    if wallet.deposit:
        print(f"  Deposit: {wallet.deposit}")
```

**Renvoie :** `List[Wallet]`

| Champ            | Type            | Description                                                                       |
| ---------------- | --------------- | --------------------------------------------------------------------------------- |
| `name`           | `str`           | Nom de la monnaie (par ex. `"bitcoin"`, `"CLORE-Blockchain"`, `"USD-Blockchain"`) |
| `balance`        | `float \| None` | Solde actuel                                                                      |
| `deposit`        | `str \| None`   | Adresse de dépôt                                                                  |
| `withdrawal_fee` | `float \| None` | Frais de retrait                                                                  |

***

### `marketplace()`

Recherchez sur la place de marché GPU avec des filtres optionnels côté client.

```python
# Tous les serveurs disponibles
servers = client.marketplace()

# Filtrer par modèle de GPU et prix max
servers = client.marketplace(
    gpu="RTX 4090",
    max_price_usd=5.0
)

# Racks multi-GPU avec beaucoup de RAM
servers = client.marketplace(
    min_gpu_count=4,
    min_ram_gb=128.0
)
```

**Paramètres :**

| Paramètre        | Type            | Par défaut | Description                                                                     |
| ---------------- | --------------- | ---------- | ------------------------------------------------------------------------------- |
| `gpu`            | `str \| None`   | `Aucun`    | Filtrer par modèle de GPU (correspondance de sous-chaîne insensible à la casse) |
| `min_gpu_count`  | `int \| None`   | `Aucun`    | Nombre minimum de GPU                                                           |
| `min_ram_gb`     | `float \| None` | `Aucun`    | RAM minimum en Go                                                               |
| `max_price_usd`  | `float \| None` | `Aucun`    | Prix maximum par heure en USD                                                   |
| `available_only` | `bool`          | `True`     | Retourner uniquement les serveurs disponibles à la location                     |

**Renvoie :** `List[MarketplaceServer]`

Chaque `MarketplaceServer` fournit des propriétés pratiques pour les champs les plus courants, plus l’accès aux données imbriquées complètes :

| Propriété        | Type            | Description                                                           |
| ---------------- | --------------- | --------------------------------------------------------------------- |
| `id`             | `int`           | ID unique du serveur                                                  |
| `gpu_model`      | `str \| None`   | Description du GPU principal (par ex. `"1x NVIDIA GeForce RTX 4090"`) |
| `gpu_count`      | `int`           | Nombre de GPU (depuis `gpu_array`)                                    |
| `ram_gb`         | `float \| None` | RAM en Go                                                             |
| `price_usd`      | `float \| None` | Prix à la demande en USD                                              |
| `spot_price_usd` | `float \| None` | Prix spot en USD                                                      |
| `available`      | `bool`          | Si le serveur est disponible (non loué)                               |
| `location`       | `str \| None`   | Code pays à partir des spécifications réseau                          |

Pour des cas d’usage avancés, vous pouvez accéder à la structure imbriquée complète :

| Champ         | Type                   | Description                                                                                                   |
| ------------- | ---------------------- | ------------------------------------------------------------------------------------------------------------- |
| `specs`       | `ServerSpecs \| None`  | Spécifications matérielles complètes (`specs.gpu`, `specs.ram`, `specs.cpu`, `specs.disk`, `specs.net`, etc.) |
| `price`       | `ServerPrice \| None`  | Objet prix complet (`price.usd.on_demand_usd`, `price.usd.spot`, `price.on_demand`, etc.)                     |
| `rented`      | `bool \| None`         | Si le serveur est actuellement loué                                                                           |
| `reliability` | `float \| None`        | Score de fiabilité du serveur                                                                                 |
| `rating`      | `ServerRating \| None` | Note du serveur (`rating.avg`, `rating.cnt`)                                                                  |

> **Remarque :** Le `marketplace()` l’endpoint est public — il fonctionne sans clé API.

***

### `my_servers()`

Lister les serveurs que vous fournissez à la place de marché Clore.ai.

```python
my_servers = client.my_servers()

for server in my_servers:
    print(f"{server.name}: {server.gpu_model} [{server.status}]")
```

**Renvoie :** `List[MyServer]`

| Propriété    | Type            | Description                                                                                 |
| ------------ | --------------- | ------------------------------------------------------------------------------------------- |
| `id`         | `int`           | ID du serveur                                                                               |
| `name`       | `str \| None`   | Nom du serveur                                                                              |
| `gpu_model`  | `str \| None`   | Description du GPU principal                                                                |
| `ram_gb`     | `float \| None` | RAM en Go                                                                                   |
| `status`     | `str`           | Statut lisible par l’humain : `"Online"`, `"Offline"`, `"Disconnected"`, ou `"Not Working"` |
| `connected`  | `bool \| None`  | Si le serveur est connecté                                                                  |
| `online`     | `bool \| None`  | Si le serveur est en ligne                                                                  |
| `visibility` | `str \| None`   | `"public"` ou `"private"`                                                                   |

***

### `server_config(server_name)`

Obtenez la configuration d’un serveur spécifique que vous hébergez.

```python
config = client.server_config("MyGPU")

print(f"Server: {config.name}")
print(f"GPU: {config.gpu_model}")
print(f"Min rental: {config.mrl}h")
print(f"On-demand: ${config.on_demand_price}")
print(f"Spot: ${config.spot_price}")
```

**Paramètres :**

| Paramètre     | Type  | Description    |
| ------------- | ----- | -------------- |
| `server_name` | `str` | Nom du serveur |

**Renvoie :** `ServerConfig`

| Propriété         | Type                  | Description                              |
| ----------------- | --------------------- | ---------------------------------------- |
| `name`            | `str \| None`         | Nom du serveur                           |
| `gpu_model`       | `str \| None`         | Description du GPU principal             |
| `mrl`             | `int \| None`         | Durée minimale de location en heures     |
| `on_demand_price` | `float \| None`       | Premier prix on-demand disponible en USD |
| `spot_price`      | `float \| None`       | Premier prix spot disponible en USD      |
| `specs`           | `ServerSpecs \| None` | Spécifications matérielles complètes     |
| `connected`       | `bool \| None`        | Si le serveur est connecté               |
| `visibility`      | `str \| None`         | `"public"` ou `"private"`                |

***

### `my_orders(include_completed)`

Obtenez vos commandes en cours, en incluant éventuellement celles terminées/expirées.

```python
# Uniquement les commandes actives
orders = client.my_orders()

# Inclure les commandes terminées
all_orders = client.my_orders(include_completed=True)

for order in orders:
    print(f"Order {order.id}: {order.type} — {order.status}")
    if order.pub_cluster:
        print(f"  IP: {order.pub_cluster}")
    if order.tcp_ports:
        print(f"  Ports: {order.tcp_ports}")
```

**Paramètres :**

| Paramètre           | Type   | Par défaut | Description                              |
| ------------------- | ------ | ---------- | ---------------------------------------- |
| `include_completed` | `bool` | `False`    | Inclure les commandes terminées/expirées |

**Renvoie :** `List[Order]`

| Champ         | Type            | Description                       |
| ------------- | --------------- | --------------------------------- |
| `id`          | `int`           | ID unique de la commande          |
| `server_id`   | `int \| None`   | ID du serveur                     |
| `type`        | `str`           | `"on-demand"` ou `"spot"`         |
| `status`      | `str \| None`   | Statut de la commande             |
| `image`       | `str \| None`   | Image Docker                      |
| `currency`    | `str \| None`   | Devise de paiement                |
| `price`       | `float \| None` | Prix de la commande par jour      |
| `pub_cluster` | `str \| None`   | Nom d’hôte/IP public pour l’accès |
| `tcp_ports`   | `dict \| None`  | Mappages de ports TCP             |

***

### `spot_marketplace(server_id)`

Voir les offres du marché spot pour un serveur spécifique.

```python
spot = client.spot_marketplace(server_id=6)

if spot.offers:
    for offer in spot.offers:
        print(f"Order {offer.order_id}: ${offer.price}/day (server {offer.server_id})")

if spot.currency_rates_in_usd:
    for coin, rate in spot.currency_rates_in_usd.items():
        print(f"  {coin}: ${rate}")
```

**Paramètres :**

| Paramètre   | Type  | Description              |
| ----------- | ----- | ------------------------ |
| `server_id` | `int` | ID du serveur à vérifier |

**Renvoie :** `SpotMarket`

| Champ                   | Type                       | Description                                              |
| ----------------------- | -------------------------- | -------------------------------------------------------- |
| `offers`                | `List[SpotOffer] \| None`  | Liste des offres spot (`order_id`, `price`, `server_id`) |
| `server`                | `SpotServerInfo \| None`   | Infos serveur (prix min, visibilité, statut en ligne)    |
| `currency_rates_in_usd` | `Dict[str, float] \| None` | Taux de change des devises en USD                        |

***

### `create_order(...)`

Créer une nouvelle commande on-demand ou spot. C’est ainsi que vous louez un GPU.

#### Commande on-demand

```python
order = client.create_order(
    server_id=123,
    image="cloreai/ubuntu22.04-cuda12",
    type="on-demand",
    currency="bitcoin",
    ssh_password="MySecurePass123",
    ports={"22": "tcp", "8888": "http"}
)

print(f"Order created: {order.id}")
print(f"Connect: {order.pub_cluster}")
```

#### Commande spot

```python
order = client.create_order(
    server_id=123,
    image="cloreai/pytorch",
    type="spot",
    currency="bitcoin",
    spot_price=0.000005,
    ssh_password="MySecurePass123",
    ports={"22": "tcp"}
)
```

**Paramètres :**

| Paramètre            | Type    | Requis          | Description                                                |
| -------------------- | ------- | --------------- | ---------------------------------------------------------- |
| `server_id`          | `int`   | Oui             | ID du serveur à louer                                      |
| `image`              | `str`   | Oui             | Image Docker (par ex. `"cloreai/ubuntu22.04-cuda12"`)      |
| `type`               | `str`   | Oui             | `"on-demand"` ou `"spot"`                                  |
| `currency`           | `str`   | Oui             | Devise de paiement (par ex. `"bitcoin"`)                   |
| `ssh_password`       | `str`   | Non             | Mot de passe SSH (alphanumérique, max 32 caractères)       |
| `ssh_key`            | `str`   | Non             | Clé publique SSH (max 3072 caractères)                     |
| `ports`              | `dict`  | Non             | Mappages de ports, par ex. `{"22": "tcp", "8888": "http"}` |
| `env`                | `dict`  | Non             | Variables d’environnement                                  |
| `jupyter_token`      | `str`   | Non             | Token du notebook Jupyter (max 32 caractères)              |
| `command`            | `str`   | Non             | Commande shell à exécuter après le démarrage du conteneur  |
| `spot_price`         | `float` | Spot uniquement | Prix par jour pour les commandes spot                      |
| `required_price`     | `float` | Non             | Verrouiller un prix spécifique (on-demand seulement)       |
| `autossh_entrypoint` | `str`   | Non             | Utiliser le point d’entrée SSH de Clore.ai                 |

**Renvoie :** `Order`

> **Limitation de débit :** `create_order` a un temps de refroidissement spécial de 5 secondes entre les appels. Le SDK l’applique automatiquement.

***

### `cancel_order(order_id, issue)`

Annuler une commande active ou une offre spot. Signalement d’un problème avec le serveur en option.

```python
# Annulation simple
client.cancel_order(order_id=38)

# Annuler avec rapport de problème
client.cancel_order(
    order_id=38,
    issue="GPU #1 was overheating and throttling"
)
```

**Paramètres :**

| Paramètre  | Type  | Requis | Description                                                     |
| ---------- | ----- | ------ | --------------------------------------------------------------- |
| `order_id` | `int` | Oui    | ID de la commande à annuler                                     |
| `issue`    | `str` | Non    | Raison d’annulation / rapport de problème (max 2048 caractères) |

**Renvoie :** `Dict[str, Any]`

***

### `set_server_settings(...)`

Mettre à jour les paramètres d’un serveur que vous hébergez sur la place de marché.

```python
client.set_server_settings(
    name="MyGPU",
    availability=True,
    mrl=96,
    on_demand=0.0001,
    spot=0.00000113
)
```

**Paramètres :**

| Paramètre      | Type    | Requis | Description                          |
| -------------- | ------- | ------ | ------------------------------------ |
| `name`         | `str`   | Oui    | Nom du serveur                       |
| `availability` | `bool`  | Non    | Si le serveur peut être loué         |
| `mrl`          | `int`   | Non    | Durée minimale de location en heures |
| `on_demand`    | `float` | Non    | Prix on-demand par jour              |
| `spot`         | `float` | Non    | Prix spot minimum par jour           |

**Renvoie :** `Dict[str, Any]`

***

### `set_spot_price(order_id, price)`

Mettre à jour le prix de votre offre sur le marché spot.

```python
client.set_spot_price(order_id=39, price=0.000003)
```

**Paramètres :**

| Paramètre  | Type    | Description                  |
| ---------- | ------- | ---------------------------- |
| `order_id` | `int`   | ID de la commande/offre spot |
| `price`    | `float` | Nouveau prix par jour        |

**Renvoie :** `Dict[str, Any]`

> **Remarque :** Vous ne pouvez baisser les prix spot qu’une fois toutes les 600 secondes, et par un pas limité. L’API renvoie `code: 6` avec des détails si vous dépassez ces limites.

***

## Client asynchrone (`AsyncCloreAI`)

Le `AsyncCloreAI` client fournit les mêmes méthodes que `CloreAI`, mais toutes renvoient des coroutines. Utilisez-le lorsque vous avez besoin d’appels API concurrents ou lorsque vous travaillez dans une application asynchrone.

### Utilisation de base

```python
import asyncio
from clore_ai import AsyncCloreAI

async def main():
    async with AsyncCloreAI(api_key="your_key") as client:
        wallets = await client.wallets()
        for w in wallets:
            print(f"{w.name}: {w.balance:.8f}")

asyncio.run(main())
```

### Opérations concurrentes

Exécutez plusieurs appels API en parallèle avec `asyncio.gather`:

```python
import asyncio
from clore_ai import AsyncCloreAI

async def compare_gpus():
    async with AsyncCloreAI() as client:
        # Rechercher plusieurs modèles de GPU simultanément
        rtx4090, rtx3090, a100 = await asyncio.gather(
            client.marketplace(gpu="RTX 4090"),
            client.marketplace(gpu="RTX 3090"),
            client.marketplace(gpu="A100"),
        )

        for name, servers in [("RTX 4090", rtx4090), ("RTX 3090", rtx3090), ("A100", a100)]:
            if servers:
                cheapest = min(s.price_usd or float('inf') for s in servers)
                print(f"{name}: {len(servers)} available, cheapest ${cheapest:.4f}/h")
            else:
                print(f"{name}: none available")

asyncio.run(compare_gpus())
```

### Méthodes disponibles

`AsyncCloreAI` prend en charge toutes les mêmes méthodes que `CloreAI`:

| Méthode                             | Description                             |
| ----------------------------------- | --------------------------------------- |
| `await wallets()`                   | Obtenir les soldes du portefeuille      |
| `await marketplace(...)`            | Rechercher sur la place de marché       |
| `await my_servers()`                | Lister vos serveurs hébergés            |
| `await server_config(name)`         | Obtenir la configuration du serveur     |
| `await my_orders(...)`              | Lister vos commandes                    |
| `await spot_marketplace(server_id)` | Obtenir les offres du marché spot       |
| `await create_order(...)`           | Créer une nouvelle commande             |
| `await cancel_order(...)`           | Annuler une commande                    |
| `await set_server_settings(...)`    | Mettre à jour les paramètres du serveur |
| `await set_spot_price(...)`         | Mettre à jour le prix spot              |

***

## Gestion des erreurs

Le SDK fournit des classes d’exception structurées pour chaque code d’erreur API.

```python
from clore_ai import CloreAI
from clore_ai.exceptions import (
    CloreAPIError,      # Classe de base pour toutes les erreurs d’API
    AuthError,          # Code 3 — clé API invalide
    RateLimitError,     # Code 5 — limite de débit dépassée
    InvalidInputError,  # Code 2 — données de requête incorrectes
    DBError,            # Code 1 — erreur de base de données
    InvalidEndpointError,  # Code 4 — endpoint invalide
    FieldError,         # Code 6 — erreur spécifique à un champ
)

client = CloreAI()

try:
    order = client.create_order(
        server_id=123,
        image="cloreai/ubuntu22.04-cuda12",
        type="on-demand",
        currency="bitcoin",
    )
except AuthError:
    print("Clé API invalide. Vérifiez votre CLORE_API_KEY.")
except RateLimitError:
    print("Limite de débit atteinte. Le SDK réessaie automatiquement, mais vous avez atteint le nombre maximal de tentatives.")
except InvalidInputError as e:
    print(f"Mauvaise requête : {e}")
except FieldError as e:
    # Les erreurs de code 6 incluent des détails dans la réponse
    print(f"Erreur de champ : {e} (détails : {e.response})")
except CloreAPIError as e:
    print(f"Erreur API : {e} (code : {e.code})")
```

### Codes d'erreur

| Code | Exception              | Description                                                           |
| ---- | ---------------------- | --------------------------------------------------------------------- |
| 0    | —                      | Succès                                                                |
| 1    | `DBError`              | Erreur de base de données                                             |
| 2    | `InvalidInputError`    | Données d'entrée invalides                                            |
| 3    | `AuthError`            | Jeton API invalide                                                    |
| 4    | `InvalidEndpointError` | Point de terminaison invalide                                         |
| 5    | `RateLimitError`       | Limite de débit dépassée                                              |
| 6    | `FieldError`           | Erreur dans un champ spécifique (voir `erreur` champ dans la réponse) |

Toutes les classes d'exception héritent de `CloreAPIError` et incluent :

* `e.code` — code d'erreur numérique
* `e.response` — dictionnaire de la réponse API complète (lorsqu'il est disponible)

***

## Limitation de débit

Le SDK inclut un limiteur de débit intégré qui applique automatiquement les limites de Clore.ai :

| Point de terminaison                 | Limite                   |
| ------------------------------------ | ------------------------ |
| La plupart des points de terminaison | **1 requête/seconde**    |
| `create_order`                       | **1 requête/5 secondes** |

Lorsque l'API renvoie une erreur de limite de débit (code 5), le SDK applique **recul exponentiel** et réessaie jusqu'à `max_retries` fois (par défaut : 3). Vous n'avez pas besoin d'ajouter `time.sleep()` entre les appels.

### Comment cela fonctionne

1. Avant chaque requête, le limiteur de débit attend que l'intervalle minimum soit écoulé.
2. `create_order` les appels imposent un temps de refroidissement supplémentaire de 5 secondes.
3. En cas d'erreurs de limite de débit, le SDK recule de façon exponentielle : 1s → 2s → 4s → ...
4. Après `max_retries` tentatives échouées, une `RateLimitError` est levée.

### Personnaliser le comportement de réessai

```python
client = CloreAI(
    max_retries=5,    # Plus de tentatives pour les scripts de longue durée
    timeout=60.0      # Délai d'attente plus long pour les connexions lentes
)
```

***

## Configuration

### Fichier de configuration

Le CLI stocke la configuration dans `~/.clore/config.json`:

```json
{
  "api_key": "votre_cle_api_ici"
}
```

### Ordre de résolution

Le SDK résout la clé API dans cet ordre :

1. `api_key` argument passé au constructeur
2. `CLORE_API_KEY` variable d'environnement
3. `api_key` champ dans `~/.clore/config.json`

### Variables d’environnement

| Variable        | Description                     |
| --------------- | ------------------------------- |
| `CLORE_API_KEY` | Clé API pour l'authentification |

***

## Prochaines étapes

* [**Référence CLI**](/clore.ai/clore.ai-eng-fr/developpeurs/cli-guide.md) — Utiliser Clore.ai depuis votre terminal
* [**REST API**](/clore.ai/clore.ai-eng-fr/pour-les-hotes/api.md) — Documentation brute de l'API pour des intégrations personnalisées
* [**On-Demand vs Spot**](/clore.ai/clore.ai-eng-fr/pour-les-locataires/on-demand-vs-spot.md) — Comprendre les modèles de tarification
* [**Images Docker disponibles**](/clore.ai/clore.ai-eng-fr/pour-les-locataires/docker-images.md) — Images préconstruites pour les charges de travail GPU


---

# 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/clore.ai/clore.ai-eng-fr/developpeurs/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.
