# SDK de Python (clore-ai)

El **clore-ai** paquete es el SDK oficial de Python para el [Clore.ai](https://clore.ai) mercado de GPU. Envuelve toda la API REST en una interfaz limpia y con tipos seguros con limitación de tasa incorporada, reintentos automáticos y manejo estructurado de errores — para que puedas concentrarte en alquilar GPUs, no en la plomería HTTP.

***

## Instalación

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

**Requisitos:** Python 3.9+

El paquete instala tanto el SDK de Python como el [`clore` CLI](/clore.ai/clore.ai-eng-es/desarrolladores/cli-guide.md).

***

## Autenticación

Obtén tu clave de API desde el [panel de control de Clore.ai](https://clore.ai) → **API** sección.

### Opción 1: Variable de entorno (recomendado)

```bash
export CLORE_API_KEY=tu_clave_api_aqui
```

El SDK lee `CLORE_API_KEY` automáticamente — no se necesitan cambios en el código.

### Opción 2: Archivo de configuración del CLI

```bash
clore config set api_key TU_API_KEY
```

Esto almacena la clave en `~/.clore/config.json`.

### Opción 3: Pasar directamente en el código

```python
from clore_ai import CloreAI

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

> ⚠️ **Importante:** La API de Clore.ai usa la `auth` cabecera para la autenticación, **no** `Authorization: Bearer`. El SDK lo maneja automáticamente.

***

## Inicio rápido

```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")
```

***

## Cliente sincrónico (`CloreAI`)

### Constructor

```python
CloreAI(
    api_key: str | None = None,       # Recurre a la variable de entorno CLORE_API_KEY / config
    base_url: str | None = None,       # Predeterminado: https://api.clore.ai/v1
    timeout: float = 30.0,             # Tiempo de espera de la solicitud en segundos
    max_retries: int = 3               # Intentos de reintento en errores de límite de tasa / red
)
```

El cliente soporta gestores de contexto para limpieza automática:

```python
with CloreAI() as client:
    wallets = client.wallets()
    # client.close() llamado automáticamente
```

***

### `wallets()`

Obtén los saldos de tus wallets y las direcciones de depósito.

```python
wallets = client.wallets()

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

**Devuelve:** `List[Wallet]`

| Campo            | Tipo            | Descripción                                                                        |
| ---------------- | --------------- | ---------------------------------------------------------------------------------- |
| `name`           | `str`           | Nombre de la moneda (p. ej. `"bitcoin"`, `"CLORE-Blockchain"`, `"USD-Blockchain"`) |
| `balance`        | `float \| None` | Saldo actual                                                                       |
| `deposit`        | `str \| None`   | Dirección de depósito                                                              |
| `withdrawal_fee` | `float \| None` | Tarifa de retiro                                                                   |

***

### `marketplace()`

Busca en el mercado de GPU con filtros opcionales del lado del cliente.

```python
# Todos los servidores disponibles
servers = client.marketplace()

# Filtrar por modelo de GPU y precio máximo
servers = client.marketplace(
    gpu="RTX 4090",
    max_price_usd=5.0
)

# Rigs con múltiples GPUs y mucha RAM
servers = client.marketplace(
    min_gpu_count=4,
    min_ram_gb=128.0
)
```

**Parámetros:**

| Parámetro        | Tipo            | Predeterminado | Descripción                                                                        |
| ---------------- | --------------- | -------------- | ---------------------------------------------------------------------------------- |
| `gpu`            | `str \| None`   | `None`         | Filtrar por modelo de GPU (coincidencia de subcadena sin distinción de mayúsculas) |
| `min_gpu_count`  | `int \| None`   | `None`         | Número mínimo de GPUs                                                              |
| `min_ram_gb`     | `float \| None` | `None`         | RAM mínima en GB                                                                   |
| `max_price_usd`  | `float \| None` | `None`         | Precio máximo por hora en USD                                                      |
| `available_only` | `bool`          | `True`         | Solo devolver servidores que están disponibles para alquilar                       |

**Devuelve:** `List[MarketplaceServer]`

Cada `MarketplaceServer` proporciona propiedades convenientes para los campos más comunes, además de acceso a los datos anidados completos:

| Propiedad        | Tipo            | Descripción                                                             |
| ---------------- | --------------- | ----------------------------------------------------------------------- |
| `id`             | `int`           | ID único del servidor                                                   |
| `gpu_model`      | `str \| None`   | Descripción de la GPU principal (p. ej. `"1x NVIDIA GeForce RTX 4090"`) |
| `gpu_count`      | `int`           | Número de GPUs (de `gpu_array`)                                         |
| `ram_gb`         | `float \| None` | RAM en GB                                                               |
| `price_usd`      | `float \| None` | Precio bajo demanda en USD                                              |
| `spot_price_usd` | `float \| None` | Precio spot en USD                                                      |
| `available`      | `bool`          | Si el servidor está disponible (no alquilado)                           |
| `location`       | `str \| None`   | Código de país según las especificaciones de la red                     |

Para casos de uso avanzados, puedes acceder a la estructura anidada completa:

| Campo         | Tipo                   | Descripción                                                                                                     |
| ------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------- |
| `specs`       | `ServerSpecs \| None`  | Especificaciones completas de hardware (`specs.gpu`, `specs.ram`, `specs.cpu`, `specs.disk`, `specs.net`, etc.) |
| `price`       | `ServerPrice \| None`  | Objeto de precio completo (`price.usd.on_demand_usd`, `price.usd.spot`, `price.on_demand`, etc.)                |
| `rented`      | `bool \| None`         | Si el servidor está actualmente alquilado                                                                       |
| `reliability` | `float \| None`        | Puntuación de fiabilidad del servidor                                                                           |
| `rating`      | `ServerRating \| None` | Calificación del servidor (`rating.avg`, `rating.cnt`)                                                          |

> **Nota:** El `marketplace()` el endpoint es público — funciona sin clave de API.

***

### `my_servers()`

Enumera los servidores que estás proporcionando al mercado de Clore.ai.

```python
my_servers = client.my_servers()

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

**Devuelve:** `List[MyServer]`

| Propiedad    | Tipo            | Descripción                                                                              |
| ------------ | --------------- | ---------------------------------------------------------------------------------------- |
| `id`         | `int`           | ID del servidor                                                                          |
| `name`       | `str \| None`   | Nombre del servidor                                                                      |
| `gpu_model`  | `str \| None`   | Descripción de la GPU principal                                                          |
| `ram_gb`     | `float \| None` | RAM en GB                                                                                |
| `status`     | `str`           | Estado legible por humanos: `"Online"`, `"Offline"`, `"Disconnected"`, o `"Not Working"` |
| `connected`  | `bool \| None`  | Si el servidor está conectado                                                            |
| `online`     | `bool \| None`  | Si el servidor está en línea                                                             |
| `visibility` | `str \| None`   | `"public"` o `"private"`                                                                 |

***

### `server_config(server_name)`

Obtén la configuración de un servidor específico que alojas.

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

**Parámetros:**

| Parámetro     | Tipo  | Descripción         |
| ------------- | ----- | ------------------- |
| `server_name` | `str` | Nombre del servidor |

**Devuelve:** `ServerConfig`

| Propiedad         | Tipo                  | Descripción                                  |
| ----------------- | --------------------- | -------------------------------------------- |
| `name`            | `str \| None`         | Nombre del servidor                          |
| `gpu_model`       | `str \| None`         | Descripción de la GPU principal              |
| `mrl`             | `int \| None`         | Duración mínima de alquiler en horas         |
| `on_demand_price` | `float \| None`       | Primer precio disponible bajo demanda en USD |
| `spot_price`      | `float \| None`       | Primer precio spot disponible en USD         |
| `specs`           | `ServerSpecs \| None` | Especificaciones completas de hardware       |
| `connected`       | `bool \| None`        | Si el servidor está conectado                |
| `visibility`      | `str \| None`         | `"public"` o `"private"`                     |

***

### `my_orders(include_completed)`

Obtén tus órdenes actuales, opcionalmente incluyendo las completadas/expiradas.

```python
# Solo órdenes activas
orders = client.my_orders()

# Incluir órdenes completadas
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}")
```

**Parámetros:**

| Parámetro           | Tipo   | Predeterminado | Descripción                           |
| ------------------- | ------ | -------------- | ------------------------------------- |
| `include_completed` | `bool` | `False`        | Incluir órdenes completadas/expiradas |

**Devuelve:** `List[Order]`

| Campo         | Tipo            | Descripción                     |
| ------------- | --------------- | ------------------------------- |
| `id`          | `int`           | ID único de la orden            |
| `server_id`   | `int \| None`   | ID del servidor                 |
| `type`        | `str`           | `"on-demand"` o `"spot"`        |
| `status`      | `str \| None`   | Estado de la orden              |
| `image`       | `str \| None`   | Imagen de Docker                |
| `currency`    | `str \| None`   | Moneda de pago                  |
| `price`       | `float \| None` | Precio de la orden por día      |
| `pub_cluster` | `str \| None`   | Hostname/IP público para acceso |
| `tcp_ports`   | `dict \| None`  | Mapeos de puertos TCP           |

***

### `spot_marketplace(server_id)`

Ver ofertas del mercado spot para un servidor específico.

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

**Parámetros:**

| Parámetro   | Tipo  | Descripción                 |
| ----------- | ----- | --------------------------- |
| `server_id` | `int` | ID del servidor a verificar |

**Devuelve:** `SpotMarket`

| Campo                   | Tipo                       | Descripción                                                              |
| ----------------------- | -------------------------- | ------------------------------------------------------------------------ |
| `offers`                | `List[SpotOffer] \| None`  | Lista de ofertas spot (`order_id`, `price`, `server_id`)                 |
| `server`                | `SpotServerInfo \| None`   | Información del servidor (precios mínimos, visibilidad, estado en línea) |
| `currency_rates_in_usd` | `Dict[str, float] \| None` | Tasas de cambio de monedas en USD                                        |

***

### `create_order(...)`

Crea una nueva orden on-demand o spot. Así es como alquilas una GPU.

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

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

**Parámetros:**

| Parámetro            | Tipo    | Requerido | Descripción                                                  |
| -------------------- | ------- | --------- | ------------------------------------------------------------ |
| `server_id`          | `int`   | Sí        | ID del servidor a alquilar                                   |
| `image`              | `str`   | Sí        | Imagen de Docker (p. ej. `"cloreai/ubuntu22.04-cuda12"`)     |
| `type`               | `str`   | Sí        | `"on-demand"` o `"spot"`                                     |
| `currency`           | `str`   | Sí        | Moneda de pago (p. ej. `"bitcoin"`)                          |
| `ssh_password`       | `str`   | No        | Contraseña SSH (alfanumérica, máximo 32 caracteres)          |
| `ssh_key`            | `str`   | No        | Clave pública SSH (máx. 3072 caracteres)                     |
| `ports`              | `dict`  | No        | Mapeos de puertos, p. ej. `{"22": "tcp", "8888": "http"}`    |
| `env`                | `dict`  | No        | Variables de entorno                                         |
| `jupyter_token`      | `str`   | No        | Token de Jupyter notebook (máx. 32 caracteres)               |
| `command`            | `str`   | No        | Comando de shell a ejecutar después de iniciar el contenedor |
| `spot_price`         | `float` | Solo spot | Precio por día para órdenes spot                             |
| `required_price`     | `float` | No        | Asegurar un precio específico (solo on-demand)               |
| `autossh_entrypoint` | `str`   | No        | Usar el punto de entrada SSH de Clore.ai                     |

**Devuelve:** `Orden`

> **Límite de tasa:** `create_order` tiene un tiempo de espera especial de 5 segundos entre llamadas. El SDK lo aplica automáticamente.

***

### `cancel_order(order_id, issue)`

Cancelar una orden activa u oferta spot. Opcionalmente reportar un problema con el servidor.

```python
# Cancelación simple
client.cancel_order(order_id=38)

# Cancelar con reporte de problema
client.cancel_order(
    order_id=38,
    issue="GPU #1 was overheating and throttling"
)
```

**Parámetros:**

| Parámetro  | Tipo  | Requerido | Descripción                                                          |
| ---------- | ----- | --------- | -------------------------------------------------------------------- |
| `order_id` | `int` | Sí        | ID de la orden a cancelar                                            |
| `issue`    | `str` | No        | Razón de la cancelación / reporte de problema (máx. 2048 caracteres) |

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

***

### `set_server_settings(...)`

Actualizar la configuración de un servidor que alojas en el mercado.

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

**Parámetros:**

| Parámetro      | Tipo    | Requerido | Descripción                          |
| -------------- | ------- | --------- | ------------------------------------ |
| `name`         | `str`   | Sí        | Nombre del servidor                  |
| `availability` | `bool`  | No        | Si el servidor puede ser alquilado   |
| `mrl`          | `int`   | No        | Duración mínima de alquiler en horas |
| `on_demand`    | `float` | No        | Precio por día bajo demanda          |
| `spot`         | `float` | No        | Precio mínimo spot por día           |

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

***

### `set_spot_price(order_id, price)`

Actualizar el precio de tu oferta en el mercado spot.

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

**Parámetros:**

| Parámetro  | Tipo    | Descripción                |
| ---------- | ------- | -------------------------- |
| `order_id` | `int`   | ID de la orden/oferta spot |
| `price`    | `float` | Nuevo precio por día       |

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

> **Nota:** Solo puedes bajar los precios spot una vez cada 600 segundos, y por un tamaño de paso limitado. La API devuelve `code: 6` con detalles si excedes estos límites.

***

## Cliente asíncrono (`AsyncCloreAI`)

El `AsyncCloreAI` client proporciona los mismos métodos que `CloreAI`, pero todos devuelven corrutinas. Úsalo cuando necesites llamadas de API concurrentes o estés trabajando dentro de una aplicación async.

### Uso básico

```python
import asyncio
from clore_ai import AsyncCloreAI

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

asyncio.run(main())
```

### Operaciones concurrentes

Ejecuta múltiples llamadas a la API en paralelo con `asyncio.gather`:

```python
import asyncio
from clore_ai import AsyncCloreAI

async def compare_gpus():
    async with AsyncCloreAI() as client:
        # Buscar múltiples modelos de GPU concurrentemente
        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étodos disponibles

`AsyncCloreAI` soporta todos los mismos métodos que `CloreAI`:

| Method                              | Descripción                              |
| ----------------------------------- | ---------------------------------------- |
| `await wallets()`                   | Obtener saldos de wallets                |
| `await marketplace(...)`            | Buscar en el mercado                     |
| `await my_servers()`                | Listar tus servidores alojados           |
| `await server_config(name)`         | Obtener configuración del servidor       |
| `await my_orders(...)`              | Listar tus órdenes                       |
| `await spot_marketplace(server_id)` | Obtener ofertas del mercado spot         |
| `await create_order(...)`           | Crear una nueva orden                    |
| `await cancel_order(...)`           | Cancelar una orden                       |
| `await set_server_settings(...)`    | Actualizar la configuración del servidor |
| `await set_spot_price(...)`         | Actualizar el precio spot                |

***

## Manejo de errores

El SDK proporciona clases de excepción estructuradas para cada código de error de la API.

```python
from clore_ai import CloreAI
from clore_ai.exceptions import (
    CloreAPIError,      # Clase base para todos los errores de la API
    AuthError,          # Código 3 — clave de API inválida
    RateLimitError,     # Código 5 — límite de tasa excedido
    InvalidInputError,  # Código 2 — datos de solicitud incorrectos
    DBError,            # Código 1 — error de base de datos
    InvalidEndpointError,  # Código 4 — endpoint inválido
    FieldError,         # Código 6 — error específico de campo
)

client = CloreAI()

try:
    order = client.create_order(
        server_id=123,
        image="cloreai/ubuntu22.04-cuda12",
        type="on-demand",
        currency="bitcoin",
    )
except AuthError:
    print("Clave de API inválida. Verifica tu CLORE_API_KEY.")
except RateLimitError:
    print("Límite de velocidad alcanzado. El SDK reintenta automáticamente, pero alcanzaste el máximo de reintentos.")
except InvalidInputError as e:
    print(f"Solicitud incorrecta: {e}")
except FieldError as e:
    # Los errores de Código 6 incluyen detalles en la respuesta
    print(f"Error de campo: {e} (detalles: {e.response})")
except CloreAPIError as e:
    print(f"Error de API: {e} (código: {e.code})")
```

### Códigos de error

| Código | Excepción              | Descripción                                                      |
| ------ | ---------------------- | ---------------------------------------------------------------- |
| 0      | —                      | Éxito                                                            |
| 1      | `DBError`              | Error de base de datos                                           |
| 2      | `InvalidInputError`    | Datos de entrada inválidos                                       |
| 3      | `AuthError`            | Token de API inválido                                            |
| 4      | `InvalidEndpointError` | Endpoint inválido                                                |
| 5      | `RateLimitError`       | Límite de velocidad excedido                                     |
| 6      | `FieldError`           | Error en un campo específico (ver `error` campo en la respuesta) |

Todas las clases de excepción heredan de `CloreAPIError` e incluyen:

* `e.code` — código numérico de error
* `e.response` — diccionario de respuesta completa de la API (cuando esté disponible)

***

## Limitación de velocidad

El SDK incluye un limitador de velocidad incorporado que aplica automáticamente los límites de Clore.ai:

| Endpoint                | Límite                     |
| ----------------------- | -------------------------- |
| La mayoría de endpoints | **1 solicitud/segundo**    |
| `create_order`          | **1 solicitud/5 segundos** |

Cuando la API devuelve un error de límite de velocidad (código 5), el SDK aplica **retroceso exponencial** y reintenta hasta `max_retries` veces (por defecto: 3). No necesitas añadir `time.sleep()` entre llamadas.

### Cómo funciona

1. Antes de cada solicitud, el limitador de velocidad espera hasta que haya transcurrido el intervalo mínimo.
2. `create_order` las llamadas imponen una pausa adicional de 5 segundos.
3. En errores por límite de velocidad, el SDK retrocede exponencialmente: 1s → 2s → 4s → ...
4. Después de `max_retries` intentos fallidos, se `RateLimitError` lanza una excepción.

### Personalizar el comportamiento de reintentos

```python
client = CloreAI(
    max_retries=5,    # Más reintentos para scripts de larga duración
    timeout=60.0      # Tiempo de espera más largo para conexiones lentas
)
```

***

## Configuración

### Archivo de configuración

El CLI almacena la configuración en `~/.clore/config.json`:

```json
{
  "api_key": "tu_api_key_aquí"
}
```

### Orden de resolución

El SDK resuelve la clave de API en este orden:

1. `api_key` argumento pasado al constructor
2. `CLORE_API_KEY` variable de entorno
3. `api_key` campo en `~/.clore/config.json`

### Variables de entorno

| Variable        | Descripción                     |
| --------------- | ------------------------------- |
| `CLORE_API_KEY` | Clave de API para autenticación |

***

## Próximos pasos

* [**Referencia CLI**](/clore.ai/clore.ai-eng-es/desarrolladores/cli-guide.md) — Usa Clore.ai desde tu terminal
* [**REST API**](/clore.ai/clore.ai-eng-es/para-anfitriones/api.md) — Documentación cruda de la API para integraciones personalizadas
* [**On-Demand vs Spot**](/clore.ai/clore.ai-eng-es/para-arrendatarios/on-demand-vs-spot.md) — Entender los modelos de precios
* [**Imágenes Docker disponibles**](/clore.ai/clore.ai-eng-es/para-arrendatarios/docker-images.md) — Imágenes preconstruidas para cargas de trabajo en 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-es/desarrolladores/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.
