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

# Guide du SDK Python

Guide complet du SDK Python — clients synchrones/asynchrones, filtrage de la place de marché, cycle de vie des commandes, marché spot, opérations de portefeuille et gestion des erreurs

{% hint style="success" %}
**Nouveau dans le SDK ?** Commencez par le [guide de démarrage rapide de 5 minutes](/guides/guides_v2-fr/premiers-pas/python-quickstart.md) d'abord.
{% endhint %}

Pour un tutoriel de démarrage rapide avec des exemples concrets, voir [Clore.ai Python SDK — Automatisez vos workflows GPU en 5 minutes](https://blog.clore.ai/cloreai-python-sdk-automate-your-gpu-workflows-in-5-minutes/)

## Installation

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

Le SDK fournit deux clients :

* **`CloreAI`** — synchrone (plus simple, adapté aux scripts)
* **`AsyncCloreAI`** — asynchrone (plus rapide pour les opérations concurrentes)

Les deux partagent les mêmes méthodes et renvoient les mêmes modèles Pydantic.

***

## Synchrone vs asynchrone — quand utiliser chacun

| Cas d’utilisation                        | Client         | Pourquoi                                               |
| ---------------------------------------- | -------------- | ------------------------------------------------------ |
| Scripts simples, tâches ponctuelles      | `CloreAI`      | Code plus simple, pas de `async/await`                 |
| Boucles de surveillance                  | `CloreAI`      | Les vérifications séquentielles fonctionnent très bien |
| Requêtes groupées sur la place de marché | `AsyncCloreAI` | Requêtes concurrentes = plus rapide                    |
| Création d’ordres en lot                 | `AsyncCloreAI` | Créer plusieurs ordres en parallèle                    |
| Applications web                         | `AsyncCloreAI` | E/S non bloquantes                                     |

### Exemple synchrone

```python
from clore_ai import CloreAI

client = CloreAI()  # Utilise la variable d’environnement CLORE_API_KEY

servers = client.marketplace(gpu="RTX 4090")
print(f"{len(servers)} serveurs trouvés")

client.close()  # Ou utilisez un gestionnaire de contexte
```

### Exemple asynchrone

```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)} serveurs trouvés")

asyncio.run(main())
```

### Gestionnaires de contexte (recommandé)

Les deux clients prennent en charge les gestionnaires de contexte pour un nettoyage automatique :

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

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

***

## Configuration du client

```python
client = CloreAI(
    api_key="your_key",      # Ou définissez la variable d’environnement CLORE_API_KEY
    base_url="https://api.clore.ai/v1",  # Point de terminaison API personnalisé
    timeout=30.0,            # Délai d’attente de la requête en secondes
    max_retries=3            # Tentatives après limitation de débit / erreurs réseau
)
```

Le SDK inclut un limiteur de débit intégré :

* **Requêtes générales :** 1 requête/seconde
* **`create_order`:** délai de 5 secondes entre les appels
* **Erreurs de limitation de débit (code 5) :** Backoff exponentiel automatique

***

## Filtrage de la place de marché

Le `marketplace()` la méthode récupère tous les serveurs disponibles et filtre côté client :

```python
from clore_ai import CloreAI

client = CloreAI()

# Tous les serveurs disponibles
all_servers = client.marketplace()

# Filtrer par modèle de GPU (correspondance de sous-chaîne insensible à la casse)
rtx_4090s = client.marketplace(gpu="RTX 4090")

# Filtrer par plusieurs critères
budget_gpus = client.marketplace(
    gpu="RTX 4090",
    max_price_usd=1.0,       # Prix max 1,00 $/heure
    min_gpu_count=2,          # Au moins 2 GPU
    min_ram_gb=64.0,          # Au moins 64 Go de RAM système
    available_only=True       # Serveurs disponibles uniquement (par défaut)
)
```

### Filtrage avancé (côté client)

Pour les filtres non intégrés à la méthode, filtrez les `Server` objets renvoyés vous-même :

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

# Serveurs en UE avec une fiabilité élevée
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
]

# Trier par prix
cheapest = sorted(servers, key=lambda s: s.price_usd or float("inf"))
print(f"Le moins cher : serveur {cheapest[0].id} — ${cheapest[0].price_usd:.4f}/h")
```

### Champs du modèle de serveur

Chaque `MarketplaceServer` objet possède ces attributs et propriétés pratiques :

| Champ            | Type                  | Description                                                            |
| ---------------- | --------------------- | ---------------------------------------------------------------------- |
| `id`             | `int`                 | ID du serveur (utilisez-le dans `create_order`)                        |
| `gpu_model`      | `str \| None`         | Description du GPU d’après les spécifications (propriété)              |
| `gpu_count`      | `int`                 | Nombre de GPU provenant de `gpu_array` (propriété)                     |
| `ram_gb`         | `float \| None`       | RAM système en Go (propriété, depuis `specs.ram`)                      |
| `price_usd`      | `float \| None`       | Prix à la demande en USD (propriété, depuis `price.usd.on_demand_usd`) |
| `spot_price_usd` | `float \| None`       | Prix spot en USD (propriété)                                           |
| `disponibles`    | `bool`                | Indique si le serveur n’est pas loué (propriété)                       |
| `location`       | `str \| None`         | Code pays issu des spécifications réseau (propriété)                   |
| `specs`          | `ServerSpecs \| None` | Spécifications matérielles (cpu, ram, disque, gpu, réseau)             |
| `price`          | `ServerPrice \| None` | Structure tarifaire complète                                           |
| `rented`         | `bool \| None`        | Indique si le serveur est actuellement loué                            |

***

## Gestion des ordres

### Création d’ordres

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="on-demand",               # "on-demand" ou "spot"
    currency="bitcoin",             # Devise de paiement
    ssh_password="MySecurePass",    # Accès SSH
    ports={"22": "tcp", "8888": "http"},  # Mappages de ports
    env={"HF_TOKEN": "hf_xxx"},    # Variables d’environnement
    command="bash /start.sh",       # Commande de démarrage personnalisée
    jupyter_token="my_token"        # Jeton du notebook Jupyter
)

print(f"ID de l’ordre : {order.id}")
print(f"IP : {order.pub_cluster}")
print(f"Ports : {order.tcp_ports}")
```

### Complet `create_order` Paramètres

| Paramètre            | Type    | Requis | Description                              |
| -------------------- | ------- | ------ | ---------------------------------------- |
| `server_id`          | `int`   | ✅      | Serveur à louer                          |
| `image`              | `str`   | ✅      | Image Docker                             |
| `type`               | `str`   | ✅      | `"on-demand"` ou `"spot"`                |
| `currency`           | `str`   | ✅      | Devise de paiement (par ex. `"bitcoin"`) |
| `ssh_password`       | `str`   | —      | Mot de passe SSH                         |
| `ssh_key`            | `str`   | —      | Clé publique SSH                         |
| `ports`              | `dict`  | —      | Mappages de ports (`{"22": "tcp"}`)      |
| `env`                | `dict`  | —      | Variables d’environnement                |
| `jupyter_token`      | `str`   | —      | Jeton du notebook Jupyter                |
| `command`            | `str`   | —      | Commande de démarrage                    |
| `spot_price`         | `float` | —      | Prix d’enchère spot                      |
| `required_price`     | `float` | —      | Prix requis                              |
| `autossh_entrypoint` | `str`   | —      | Point d’entrée Auto SSH                  |

### Liste des ordres

```python
# Ordres actifs uniquement
active = client.my_orders()
for o in active:
    print(f"Ordre {o.id} : type={o.type}, IP={o.pub_cluster}, statut={o.status}")

# Inclure les ordres terminés
all_orders = client.my_orders(include_completed=True)
```

### Champs du modèle d’ordre

| Champ         | Type            | Description                                 |
| ------------- | --------------- | ------------------------------------------- |
| `id`          | `int`           | ID de l’ordre                               |
| `server_id`   | `int \| None`   | ID du serveur loué                          |
| `type`        | `str`           | `"on-demand"` ou `"spot"`                   |
| `status`      | `str \| None`   | Statut de l’ordre                           |
| `image`       | `str \| None`   | Image Docker                                |
| `currency`    | `str \| None`   | Devise de paiement                          |
| `price`       | `float \| None` | Prix                                        |
| `pub_cluster` | `str \| None`   | IP publique / nom d’hôte                    |
| `tcp_ports`   | `dict \| None`  | Mappages de ports (par ex. `{"22": 50022}`) |
| `created_at`  | `str \| None`   | Horodatage de création                      |

### Surveillance des ordres

```python
import time

def wait_for_ready(client, order_id, timeout=120):
    """Attendre qu’un ordre obtienne une IP publique."""
    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"L’ordre {order_id} n’est pas prêt après {timeout}s")

# Utilisation
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)}")
```

### Annulation des ordres

```python
# Annuler avec un motif facultatif
client.cancel_order(order_id=38, issue="Job complete")

# Annuler tous les ordres actifs
orders = client.my_orders()
for order in orders:
    client.cancel_order(order.id, issue="Cleanup")
    print(f"Ordre {order.id} annulé")
```

***

## Gestion des serveurs (pour les hébergeurs)

Si vous hébergez des GPU sur Clore, le SDK vous permet de gérer vos serveurs :

### Lister vos serveurs

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

### Obtenir la configuration du serveur

```python
config = client.server_config("MyGPU-Rig")
print(f"Nom : {config.name}")
print(f"Visibilité : {config.visibility}")
print(f"En ligne : {config.online}")
print(f"Loyer min. : {config.mrl}h")
print(f"Prix à la demande : {config.on_demand_price}")
print(f"Prix spot : {config.spot_price}")
```

### Mettre à jour les paramètres du serveur

```python
client.set_server_settings(
    name="MyGPU-Rig",
    availability=True,       # Rendre le serveur disponible
    mrl=24,                  # Location minimale de 24 h
    on_demand=0.0001,        # Prix à la demande en BTC
    spot=0.00000113          # Prix spot en BTC
)
print("Paramètres mis à jour")
```

***

## Marché spot

Les ordres spot peuvent être interrompus si quelqu’un surenchérit sur vous. Environ un tiers des serveurs affichent un prix spot inférieur au prix à la demande (médiane \~13 % de moins) ; les autres affichent le spot au prix à la demande.

### Voir les offres spot

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

### Créer un ordre spot

```python
order = client.create_order(
    server_id=142,
    image="cloreai/ubuntu22.04-cuda12",
    type="spot",
    currency="bitcoin",
    spot_price=0.0001,       # Votre prix d’enchère
    ssh_password="MyPass"
)
print(f"Ordre spot {order.id} créé")
```

### Ajuster le prix spot

```python
# Augmentez votre enchère pour éviter d’être dépassé
client.set_spot_price(order_id=39, price=0.000003)
```

### Stratégie d’enchère spot

```python
from clore_ai import CloreAI

client = CloreAI()

def smart_spot_bid(server_id, premium_pct=5):
    """Enchérissez légèrement au-dessus du prix spot minimum actuel."""
    offers = client.spot_marketplace(server_id=server_id)
    if not offers:
        print("Aucune offre spot — utilisez le prix à la demande comme base")
        return None

    min_price = min(o["price"] for o in offers)
    bid = min_price * (1 + premium_pct / 100)
    print(f"Min du marché : {min_price}, enchère : {bid:.8f} (+{premium_pct}%)")
    return bid

# Utilisation
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
    )
```

***

## Opérations sur les portefeuilles

### Vérifier les soldes

```python
wallets = client.wallets()
for w in wallets:
    print(f"{w.name} : {w.balance:.8f}")
    if w.deposit:
        print(f"  Adresse de dépôt : {w.deposit}")
```

### Alerte de solde faible

```python
from clore_ai import CloreAI

def check_balance(min_btc=0.001):
    """Alerter si le solde BTC est inférieur au seuil."""
    client = CloreAI()
    wallets = client.wallets()

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

    print("✅ Soldes OK")
    return True

check_balance(min_btc=0.001)
```

***

## Bonnes pratiques de gestion des erreurs

### Hiérarchie des exceptions

```
CloreAPIError (base)
├── DBError           (code 1) — erreur de base de données
├── InvalidInputError (code 2) — entrée invalide
├── AuthError         (code 3) — clé API invalide
├── InvalidEndpointError (code 4) — mauvais point de terminaison
├── RateLimitError    (code 5) — limitation de débit (nouvelle tentative automatique)
└── FieldError        (code 6) — erreur spécifique à un champ
```

### Gestion des erreurs de base

```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("Clé API invalide — vérifiez CLORE_API_KEY")
except InvalidInputError as e:
    print(f"Entrée incorrecte : {e}")
except RateLimitError:
    print("Limitation de débit — le SDK réessaie automatiquement, mais le nombre maximal de tentatives a été dépassé")
except CloreAPIError as e:
    print(f"Erreur API (code {e.code}) : {e}")
```

### Modèle de nouvelle tentative avec backoff

Le SDK intègre des tentatives automatiques pour les limites de débit et les erreurs réseau (`max_retries=3`). Pour les tentatives au niveau de l'application :

```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):
    """Réessayer une opération de l'API Clore avec backoff exponentiel."""
    for attempt in range(max_attempts):
        try:
            return func()
        except RateLimitError:
            if attempt < max_attempts - 1:
                delay = base_delay * (2 ** attempt)
                print(f"Limite de débit atteinte, nouvelle tentative dans {delay}s...")
                time.sleep(delay)
            else:
                raise
        except CloreAPIError as e:
            if e.code in (1,):  # Les erreurs BD peuvent être transitoires
                if attempt < max_attempts - 1:
                    time.sleep(base_delay)
                    continue
            raise

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

***

## Conseils de performance

### 1. Réutiliser le client

```python
# ❌ Mauvais — crée une nouvelle connexion HTTP à chaque fois
for _ in range(10):
    client = CloreAI()
    client.marketplace()
    client.close()

# ✅ Bon — réutilise la connexion HTTP
client = CloreAI()
for _ in range(10):
    client.marketplace()
client.close()
```

### 2. Utiliser l'asynchrone pour les opérations concurrentes

```python
import asyncio
from clore_ai import AsyncCloreAI

async def compare_gpus():
    async with AsyncCloreAI() as client:
        # Lancer 3 recherches en parallèle
        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)} serveurs")
        print(f"RTX 3090 : {len(rtx3090)} serveurs")
        print(f"A100 : {len(a100)} serveurs")

asyncio.run(compare_gpus())
```

### 3. Création asynchrone de lots de commandes

```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"Serveur {sid} : ÉCHEC — {result}")
            else:
                print(f"Serveur {sid} : commande {result.id} créée")

        return orders

# Déployer sur 3 serveurs en même temps
asyncio.run(batch_deploy([142, 305, 891]))
```

{% hint style="warning" %}
**Remarque :** Le SDK impose un délai de refroidissement de 5 secondes entre `create_order` les appels. Même en mode asynchrone, les commandes sont espacées pour respecter les limites de débit.
{% endhint %}

### 4. Fermer les clients une fois terminé

```python
# Le gestionnaire de contexte s'en charge automatiquement
with CloreAI() as client:
    # travail...
    pass  # client.close() appelé automatiquement

# Ou fermer manuellement
client = CloreAI()
try:
    # travail...
    pass
finally:
    client.close()
```

***

## Exemple complet : mise à l'échelle automatique des workers 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"
):
    """Maintenir un pool de workers GPU."""
    async with AsyncCloreAI() as client:
        # 1. Vérifier les commandes actuelles
        current_orders = await client.my_orders()
        active_count = len(current_orders)
        print(f"Workers actifs : {active_count}/{target_workers}")

        if active_count >= target_workers:
            print("Déjà à l'objectif. Rien à faire.")
            return

        # 2. Trouver les serveurs disponibles
        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"Seulement {len(candidates)} serveurs disponibles (il en faut {needed})")

        # 3. Déployer
        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"Déployé sur le serveur {server.id} → commande {order.id}")
            except CloreAPIError as e:
                print(f"Échec du déploiement sur {server.id} : {e}")

asyncio.run(auto_scale())
```

***

## Étapes suivantes

* [Automatisation CLI](/guides/guides_v2-fr/avance/cli-automation.md) — scripts Bash, CI/CD, opérations par lots
* [Traitement par lots](/guides/guides_v2-fr/avance/batch-processing.md) — Traiter de gros volumes de travail sur les GPU Clore
* [Intégration API](/guides/guides_v2-fr/avance/api-integration.md) — Connecter les services IA à vos applications


---

# 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-fr/avance/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.
