Metadata-Version: 2.5
Name: iocloud
Version: 0.2.0
Summary: Python SDK and CLI for io.net Cloud (CaaS): serving, fine-tuning, notebooks, RLVR on a decentralised GPU network
Project-URL: Homepage, https://github.com/ionet-official/iocloud
Project-URL: Documentation, https://iocloud.readthedocs.io
Project-URL: Repository, https://github.com/ionet-official/iocloud.git
Project-URL: Issues, https://github.com/ionet-official/iocloud/issues
Author-email: "io.net" <gurunath@io.net>
License-File: LICENSE
Keywords: caas,cloud,container,gpu,iocloud
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Distributed Computing
Requires-Python: >=3.10
Requires-Dist: click>=8.1.0
Requires-Dist: cryptography>=40.0.0
Requires-Dist: httpx>=0.24.0
Requires-Dist: keyring>=24.0.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich-click>=1.7.0
Requires-Dist: rich>=13.0.0
Requires-Dist: typing-extensions>=4.5.0
Provides-Extra: all
Requires-Dist: boto3>=1.34.0; extra == 'all'
Requires-Dist: openai>=1.30.0; extra == 'all'
Requires-Dist: questionary>=2.0.1; extra == 'all'
Requires-Dist: textual-plotext>=0.2.1; extra == 'all'
Requires-Dist: textual>=0.41.0; extra == 'all'
Provides-Extra: data
Requires-Dist: boto3>=1.34.0; extra == 'data'
Provides-Extra: dev
Requires-Dist: mypy>=1.0.0; extra == 'dev'
Requires-Dist: pre-commit>=3.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
Requires-Dist: pytest-mock>=3.10.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: docs
Requires-Dist: mkdocs-material>=9.5.0; extra == 'docs'
Provides-Extra: openai
Requires-Dist: openai>=1.30.0; extra == 'openai'
Provides-Extra: tui
Requires-Dist: questionary>=2.0.1; extra == 'tui'
Requires-Dist: textual-plotext>=0.2.1; extra == 'tui'
Requires-Dist: textual>=0.41.0; extra == 'tui'
Description-Content-Type: text/markdown

# iocloud

[![CI](https://img.shields.io/badge/ci-github%20actions-blue)](https://github.com/ionet-official/iocloud/actions)
[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)](https://www.python.org/)
![License](https://img.shields.io/badge/license-MIT-green)
[![Status](https://img.shields.io/badge/status-pre--1.0-orange)](#project-status)

Python SDK and CLI for [io.net](https://io.net) compute: run containers, serve
models behind an OpenAI-compatible API, fine-tune them, and open a GPU notebook —
on a decentralised GPU network, from one tool.

There is exactly one primitive underneath: a **CaaS deployment**, a prepaid,
time-boxed cluster running N identical replicas of one container image. Every
product in this SDK is that primitive with a job-shaped wrapper around it.

| Product | What you get | CLI | Python |
| --- | --- | --- | --- |
| Endpoints | vLLM/SGLang behind an OpenAI-compatible API, authenticated by default | `iocloud endpoint` | `client.endpoints` |
| Fine-tuning | A TRL job (SFT/DPO/GRPO) with an S3 data plane | `iocloud finetune` | `client.finetune` |
| Notebooks | Jupyter on a GPU box, token generated locally | `iocloud notebook` | `client.notebooks` |
| Recipes | Curated, ready-to-deploy model configs (consumer GGUF to datacenter) | `iocloud recipe` | `iocloud.recipes` |
| RLVR | Colocated GRPO with verifiable rewards | `iocloud rlvr` | `client.rlvr` |
| Raw deployments | Any container you can push to a registry | `iocloud deploy` | `client.deployments` |

## Install

Python 3.10 or newer.

```bash
git clone https://github.com/ionet-official/iocloud.git
cd iocloud
pip install -e .
```

Optional extras: `iocloud[openai]` (`Endpoint.openai_client()`),
`iocloud[data]` (`iocloud data push/pull`, fine-tune result verification),
`iocloud[tui]`, or `iocloud[all]`.

## Sixty seconds to a running model

```bash
iocloud auth login                    # paste an API token from the io.net console

# Price it before you spend: every mutating command takes --dry-run.
iocloud endpoint deploy --model Qwen/Qwen3-8B --gpu H100:8 --hours 1 --dry-run

# Deploy for real. This blocks until the server answers its health path,
# not merely until the platform says "running".
iocloud endpoint deploy --model Qwen/Qwen3-8B --gpu H100:8 --hours 1

iocloud endpoint list
iocloud endpoint call <endpoint-id> --prompt "Write a haiku about GPUs."
iocloud endpoint delete <endpoint-id>

iocloud dash                          # live dashboard, with pip install 'iocloud[tui]'
```

`--hours` is **prepaid**: you are billed up front and deleting early refunds
nothing, it only frees the capacity. That is why `--dry-run` prints a live quote
before anything is charged.

The same thing from Python:

```python
import iocloud

with iocloud.Client() as io:                      # IOCLOUD_API_KEY, or ~/.iocloud
    endpoint = io.endpoints.create(model="Qwen/Qwen3-8B", gpu="H100:8", hours=1)
    endpoint.wait()                             # running + URL + health 200
    print(endpoint.url)

    client = endpoint.openai_client()           # needs iocloud[openai]
    answer = client.chat.completions.create(
        model="Qwen/Qwen3-8B",
        messages=[{"role": "user", "content": "Write a haiku about GPUs."}],
    )
    print(answer.choices[0].message.content)

    endpoint.delete()
```

Deploy your own container instead:

```python
import iocloud

io = iocloud.Client()
deployment = io.deployments.create(
    name="web",
    image="nginx:latest",
    gpu="H100:8",
    hours=1,
    port=80,
)
deployment.wait()
print(deployment.url())
for line in deployment.logs(follow=True):
    print(line.text)
```

`iocloud.AsyncClient` is the 1:1 async twin — same names, same arguments, `await`
in front.

## Recipes

A **recipe** is a curated, ready-to-deploy serving config for a model: it pins the
inference server, the concrete GPU hardware, the GPU count, and the server flags
that make that model fast and fit in memory. Deploy one instead of remembering the
right `--gpu`, `--max-model-len` and quantization per model.

```bash
iocloud recipe list                        # all recipes (bundled + your own)
iocloud recipe list --task coding          # filter by use-case
iocloud recipe show qwen3.6-27b            # the full config + rendered flags

iocloud endpoint deploy --recipe qwen3.6-27b       # deploy from a recipe
iocloud endpoint deploy --model Qwen/Qwen3.6-27B   # resolve that model's default recipe
```

The bundled catalogue spans **consumer GPUs** (quantized GGUF via llama.cpp) up to
**datacenter** cards (vLLM on H100/H200/B200), across `chat`, `coding`, `vision`,
`reasoning` and `video` tasks, and is updated as new models ship.

A model can carry several **variants** (`name@variant`) for different hardware
tiers, and every recipe field is just a default — any flag you pass wins:

```bash
iocloud endpoint deploy --recipe qwen3.6-27b@h100    # a specific tier
iocloud endpoint deploy --recipe qwen3.6-27b --gpu H100:1   # consumer recipe on a bigger card
```

Overriding `--gpu` swaps only the hardware; the image and server stay put, so a
consumer (GGUF) recipe deploys unchanged on a larger card — the extra VRAM is
headroom.

From Python, and to add your own recipes:

```python
import iocloud

io = iocloud.Client()
endpoint = io.endpoints.create(recipe="qwen3.6-27b")   # overrides win, e.g. gpu="H100:1"
endpoint.wait()
print(endpoint.url)
```

Drop a YAML file in `~/.iocloud/recipes/` to add a recipe or shadow a bundled one
of the same `name[@variant]`; `iocloud recipe list` marks yours `SOURCE = user`.
See **[Recipes](docs/recipes.md)** for the schema and every field.

## Documentation

Full docs live in [`docs/`](docs/index.md) and build into a site with
`make docs-serve` (needs `pip install -e ".[docs]"`).

- [Quickstart](docs/quickstart.md) — install, authenticate, deploy, call.
- [Authentication](docs/authentication.md) — API tokens, storage, per-product slots.
- [Deployments](docs/deployments.md) — the CaaS primitive, waiting, logs, YAML.
- [Endpoints](docs/endpoints.md), [Recipes](docs/recipes.md),
  [Fine-tuning](docs/finetuning.md), [Notebooks](docs/notebooks.md),
  [RLVR](docs/rlvr.md) — the products.
- [Data and S3](docs/data-s3.md) — how bytes reach a container with no volumes.
- [CLI reference](docs/cli.md) · [SDK reference](docs/sdk.md)
- **[Limits](docs/limits.md)** — every hard constraint in one place. Read this
  before you design an architecture around io.net compute.

Runnable examples are in [`examples/`](examples).

## Project status

Pre-1.0, and honest about what that means:

- The SDK and CLI are **feature-complete for the surface documented above**;
  anything not in the docs does not exist yet.
- API models are **hand-written against the live backend**, not generated — the
  bundled OpenAPI schema is stale. Server-side changes can therefore drift; the
  SDK tolerates unknown enum values rather than crashing, and an optional nightly
  live-smoke workflow is the only drift detector.
- Product bookkeeping (which deployment is an "endpoint") lives in a **local
  state file**, because the API has no tags. It is best-effort and documented as
  such: `iocloud deployment list` always shows the ground truth.
- Breaking changes are possible before 1.0.

Not supported, by platform constraint rather than by omission: volumes and FUSE
mounts, more than one port per deployment, autoscaling or scale-to-zero, and
east-west networking between deployments. See [Limits](docs/limits.md).

## Development

```bash
make dev          # pip install -e ".[dev,tui]"
make check        # ruff + mypy + pytest with the coverage gate
make test         # just the offline suite
make docs-serve   # preview the documentation site
```

Tests are offline by default: a `FakeCaaS` fixture simulates the deployment
lifecycle, so the suite never touches the network or your credentials. The
opt-in live smoke tests (`make test-live`) need a real token.

Contributions are welcome — small, reviewable pull requests with tests, please.
Every code sample in this README and in `docs/` is compiled by
`tests/docs/test_examples.py`, so keep them real.

## License

Released under the MIT License.
