Metadata-Version: 2.1
Name: verlex
Version: 0.19.0
Summary: One line of Python. Every cloud's best GPU price.
Author: Verlex Team
Maintainer: Verlex Team
License: Apache-2.0
Project-URL: Homepage, https://verlex.dev
Project-URL: Documentation, https://verlex.dev/docs
Keywords: cloud,serverless,machine-learning,gpu,cloud-computing,distributed-computing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
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 :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: System :: Distributed Computing
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
Classifier: Framework :: AsyncIO
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: cloudpickle>=3.0.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: pynvml>=11.5.0
Provides-Extra: agent
Requires-Dist: psutil>=5.9.0; extra == "agent"
Provides-Extra: cli
Requires-Dist: rich>=13.0.0; extra == "cli"
Provides-Extra: dev
Requires-Dist: pytest>=7.0.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
Requires-Dist: black>=23.0.0; extra == "dev"
Requires-Dist: ruff>=0.1.0; extra == "dev"
Requires-Dist: mypy>=1.0.0; extra == "dev"
Requires-Dist: sentry-sdk>=1.0.0; extra == "dev"
Provides-Extra: mcp
Requires-Dist: mcp<2,>=1.9; extra == "mcp"
Provides-Extra: overflow
Requires-Dist: psutil>=5.9.0; extra == "overflow"

# Verlex

**One line of Python. Every cloud's best GPU price.**

Verlex reads any Python function, picks the hardware it needs, prices it live across **10 clouds**, and runs it on the cheapest one. If a provider fails or stocks out, the job restarts on the next cheapest, automatically. No hardware picking. No quotas. No DevOps.

The provider's GPU price passes through **at cost**. Verlex adds a fixed service fee per GPU-hour, prorated per second. That is the whole model: no percentage markup, no hidden margin.

## Installation

```bash
pip install verlex
```

Requires Python 3.10, 3.11, 3.12, or 3.13. Grab your API key from the [dashboard](https://verlex.dev).

## Quick Start

```python
import verlex

# Define your function
def train_model():
    import torch
    model = torch.nn.Linear(100, 10)
    # Your training code here...
    return {"accuracy": 0.95}

# Run it on a cloud GPU - that's it!
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")
print(result)
```

## Basic Usage

### Pass your API key

Every function accepts `api_key` directly, no context manager needed. Set `VERLEX_API_KEY` in your environment to omit it entirely.

```python
import verlex

result = verlex.cloud(my_function, api_key="gw_your_key")
```

### Passing inputs

Your function's inputs come right after the function. A function that takes a single input accepts a bare value; a function that takes two or more inputs must receive them as one list.

```python
# No inputs
verlex.cloud(train_model)

# One input, a bare value is fine
verlex.cloud(square, 5)

# Two or more inputs, pass them as a single list
verlex.cloud(train, [dataset, epochs])
```

### Specifying resources

Override auto-detected resources by listing what you need after the inputs. Each value is recognized by its shape, so the order never matters.

```python
# 4 vCPUs, 16 GB RAM, one L40S; units make each value unambiguous
result = verlex.cloud(train_model, [data], "4 vCPUs", "16 GB", "L40S")

# Pin to a provider (a provider name is recognized by shape)
result = verlex.cloud(train_model, [data], "H100", "runpod")

# Offer alternatives: Verlex uses whichever is cheapest and available
result = verlex.cloud(train_model, [data], "H100", "A100")
```

Prefer explicit keywords? They work too, and take precedence over loose values.

```python
result = verlex.cloud(
    train_model,
    [data],
    gpu="A100",        # GPU type
    cpu=8,             # vCPU cores
    memory="64GB",     # memory
    provider="aws",    # pin to a cloud (omit to let Verlex pick the cheapest)
    timeout=7200,      # 2 hour timeout
    pip_packages=["numpy==1.26.4"],  # extra packages, always installed
    python_version="3.11",  # match your local Python
)
```

## Execution Modes

One flag controls your price-speed tradeoff.

| Mode | Flag | Behavior |
|------|------|----------|
| **Performance** | `fast=True` | Immediate execution. Best for time-sensitive workloads. |
| **Standard** | `fast=False` (default) | Up to 10 min wait for the lowest price. Best for batch jobs and cost-sensitive work. |

```python
# Performance mode, immediate execution
result = verlex.cloud(my_function, api_key="gw_your_key", fast=True)

# Standard mode (default), wait for the lowest price
result = verlex.cloud(my_function, api_key="gw_your_key")
```

Performance mode (`fast=True`) requires the Performance plan ($10/mo). Standard mode is available on every plan.

## Pricing & Billing

Two parts: a monthly plan, then a per-job cost. Verlex passes the provider's live hardware price through at cost and adds a fixed service fee per GPU-hour, prorated per second. There is no percentage markup on the hardware.

The service-fee tier is derived from the hardware's FP16 tensor performance (TFLOPS):

| Tier | Example hardware | Standard (`fast=False`) | Performance (`fast=True`) |
|------|------------------|-------------------------|---------------------------|
| CPU (CPU-only jobs) | No GPU | $0.02/hr | $0.02/hr |
| Small (<185 TFLOPS) | T4, L4, A10, RTX 3090 | $0.10/GPU-hr | $0.20/GPU-hr |
| Mid (<600 TFLOPS) | A100, L40S, RTX 4090 | $0.30/GPU-hr | $0.45/GPU-hr |
| Large (<1500 TFLOPS) | H100, H200, MI300X | $0.40/GPU-hr | $0.60/GPU-hr |
| Flagship (≥1500 TFLOPS) | B200, B300, GB200 | $0.50/GPU-hr | $0.75/GPU-hr |

The service fee is charged per GPU-hour, so an 8-GPU job pays it 8 times. There is no cold-start surcharge; a cold machine and a warm one cost the same.

- **Per-second billing, 1 second minimum.** Both the provider cost and the service fee are prorated to the second. You pay only for the time your job actually runs.
- **Prepaid credits.** Buy credits in the dashboard (minimum top-up $10). Each job places a hold and settles the exact amount when it finishes.
- **Auto top-up (opt-in).** Adds credits with your saved card when the balance runs low (default: $50 added when it drops below $10).
- **Funds-based execution.** There is no maximum job runtime. A job runs until your credits are exhausted, it stops making progress, or it finishes. The optional `timeout` argument is a client-side wait bound.

### Plans

| Plan | Price | Includes |
|------|-------|----------|
| **Standard** | $0/mo, free forever | Standard mode, prepaid credits, per-second billing, 25 GB storage |
| **Performance** | $10/mo | Everything in Standard, plus fast mode (`fast=True`) and 300 GB storage |
| **Enterprise** | Custom | Organization teams with a shared credit pool, 1 TB+ storage |

Per-second billing, no credit card required to join.

## Providers, Failover & Serverless

Every job is priced across **10 clouds** (AWS, GCP, Azure, Verda, RunPod, Vast.ai, JarvisLabs, Hyperstack, TensorDock, Lyceum) plus **four serverless container lanes** (Beam, Northflank, Novita, Cerebrium), and routed to the cheapest machine that fits.

```python
import verlex

# Default: priced across 10 clouds, routed to the cheapest machine that fits
result = verlex.cloud(train_model, gpu="A100", api_key="gw_your_key")

# Provider list: cost comparison and failover stay inside your subset
result = verlex.cloud(
    train_model,
    gpu="A100",
    provider=["runpod", "gcp", "verda"],
    api_key="gw_your_key",
)

# Hard pin: one provider, no cross-provider failover if it is out of stock
result = verlex.cloud(train_model, gpu="A100", provider="aws", api_key="gw_your_key")
```

- **Automatic failover.** If a launch fails or a region is out of stock, Verlex fails over: nearby regions first, then other providers, then substitute GPUs, always cheapest first. Your job does not fail because one cloud ran out of capacity.
- **Pinning.** A single provider pins hard with no failover. Prefer a provider list, which keeps cost comparison and failover inside your chosen subset.
- **Serverless.** Small jobs route to serverless container lanes (billed per second) when that is cheaper than a VM. This happens automatically; nothing to configure.

## Files & Workspace Sync

Send local files up to the VM before your job runs and bring generated files back when it finishes. Pass a `Workspace` to any cloud call. Auto mode mirrors your project root (respecting `.gitignore`) and returns whatever your code creates or changes; or list explicit `Upload` and `Output` specs for full control.

```python
import verlex
from verlex import Workspace

def train():
    from pathlib import Path
    Path("runs").mkdir(exist_ok=True)
    Path("runs/loss.json").write_text('{"loss": 0.42}')
    Path("model.pt").write_text("weights")
    return {"status": "done"}

# Auto mode mirrors your project root up to the VM, then returns
# new and changed files to the same paths locally
result = verlex.cloud(train, workspace=Workspace(), gpu="A100", api_key="gw_your_key")

# ./runs/loss.json and ./model.pt now exist locally
```

Your function runs inside the synced workspace, so relative paths like `open("data/train.csv")` work unchanged. Import `Workspace`, `Upload`, and `Output` from the top-level `verlex` package.

## Dependencies

Verlex scans your function's source, pins the versions of imported packages installed locally, and bundles local `.py` modules automatically. When you need certainty, pass `pip_packages`: those packages are authoritative, always installed, and a failed install fails the job loudly instead of continuing silently.

```python
result = verlex.cloud(
    train,
    api_key="gw_your_key",
    pip_packages=["torch==2.3.1", "numpy==1.26.4"],
    python_version="3.11",  # 3.10, 3.11, 3.12, or 3.13
)
```

## Pre-warming

Kill the cold start before it happens. `verlex.prewarm(fn)` analyzes your function, boots the right hardware, and hands you back a handle once the machine is ready. Calling the handle runs your function with no provisioning wait, and the machine stays attached between calls, so every call is warm.

```python
import verlex

def train(batch):
    import torch
    ...

wf = verlex.prewarm(train, gpu="A100", api_key="gw_your_key")  # A100 starts booting NOW

data = load_and_clean()      # local preprocessing runs meanwhile

result = wf(data)            # runs train(data) on the warm machine
more = wf(other_data)        # still warm, the VM stays attached

wf.release()                 # or use `with verlex.prewarm(train, "A100") as wf:`
```

## Automatic Cloud Offloading

Don't want to manage when code runs in the cloud? Let Verlex decide. `verlex.overflow()` watches CPU, memory, and GPU. When usage exceeds 85%, heavy functions are transparently offloaded to the cheapest cloud provider; everything else keeps running locally.

```python
import verlex

verlex.overflow(api_key="gw_your_key")

# Your code runs normally.
# When CPU, memory, or GPU exceeds 85%, functions go to the cloud.
data = load_data()
result = train_model(data)   # system overloaded? → cloud
evaluate(result)             # resources free → runs locally
```

Install the monitoring dependency with `pip install 'verlex[overflow]'`.

## Checkpoints & Spot Recovery

Spot capacity is 60-80% cheaper than on-demand, but the provider can reclaim it at any time. Anything your job writes into the checkpoint directory syncs to object storage while it runs. When a spot instance is reclaimed, the job is re-enqueued on the next cheapest capacity, the checkpoint directory is restored before your code runs again, and the reclaimed time is not billed. HuggingFace Trainer, PyTorch Lightning, and Keras jobs resume with no code changes.

## Available GPUs

Request a GPU by name (`gpu="A100"`, or as a spec value). Verlex finds the cheapest available instance across all 10 clouds: T4, L4, A10, V100, A100, L40S, H100, H200, B200, B300, and consumer RTX cards. If you don't specify a GPU, Verlex auto-detects your code's needs and picks the best option.

## Authentication

```python
import verlex

# Pass api_key directly to any function
result = verlex.cloud(my_function, api_key="gw_your_key")

# Or use the environment variable (VERLEX_API_KEY)
result = verlex.cloud(my_function)  # picks up from env
```

Both `gw_live_` and `gw_test_` prefixes only name the key type; every key runs against the live service with live billing.

## Error Handling

Verlex raises specific exceptions so you can handle failures gracefully:

```python
from verlex.errors import (
    VerlexError,              # Base class for all Verlex errors
    AuthenticationError,      # Invalid or missing credentials
    InsufficientCreditsError, # Not enough credits to run the job
    ProviderMaintenanceError, # Pinned provider is under maintenance
    JobFailedError,           # Job execution failed in the cloud
    JobTimeoutError,          # Job exceeded its timeout
    SerializationError,       # Function could not be serialized
    NetworkError,             # Connection to Verlex API failed
    RateLimitError,           # Too many requests
)
```

## Links

- **Website**: [verlex.dev](https://verlex.dev)
- **Documentation**: [verlex.dev/docs](https://verlex.dev/docs)

## Contact

- **Support**: support@verlex.dev
- **Sales**: sales@verlex.dev
- **General**: contact@verlex.dev

## License

Apache 2.0
