Metadata-Version: 2.4
Name: elasticgrid
Version: 0.1.0
Summary: Cross-cloud elastic training grid for variable-size workloads
License: Apache-2.0
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software 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: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: iroh-tinker>=0.3.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.5; extra == "dev"
Requires-Dist: mypy>=1.10; extra == "dev"
Dynamic: license-file

# elasticgrid

Cross-cloud elastic training grid for variable-size workloads, built on
[iroh-tinker](https://github.com/scairesearch/iroh_tinker).

elasticgrid is the control plane in front of iroh-tinker's multi-cloud launch
layer. It answers two questions:

* **How big should this job be right now?** A rolling window of training
  steps (step time, queue time, throughput) feeds a scheduler that grows the
  job when workers are waiting and shrinks it when the job is comfortably
  faster than the target, with bounds, a cooldown and a deadline rule.
* **Where should the nodes run?** A placement plan spreads the nodes over the
  available providers, respects a spot/on-demand budget, costs the placement
  and renders it as an iroh-tinker `LaunchSpec`.

Nothing here provisions anything by itself: the placement is a plan, and
iroh-tinker's `JobManager` executes it.

## Install

```bash
pip install elasticgrid
pip install -e ".[dev]"           # from a checkout
```

elasticgrid depends on the public `iroh-tinker` surface only.

## Built on the iroh-tinker public surface

| API | Used for |
|---|---|
| `iroh_tinker.cloud.spec.LaunchSpec` / `ResourcesSpec` / `RendezvousSpec` | the launch spec a placement renders into |
| `iroh_tinker.cloud.job.JobManager` | executing the plan |
| `iroh_tinker.hw.HardwareContext` / `infer_topology` / `select_topology_params` | topology hints for the placement |

`iroh_tinker.AllreduceAlgorithm` is **not** exported from the top-level
package; it lives in `iroh_tinker.comm`. elasticgrid does not depend on it.

## CLI

```bash
elasticgrid providers                        # the configured grid
elasticgrid plan --nodes 64                 # placement + cost
elasticgrid plan --nodes 64 --max-spot-fraction 0.5
elasticgrid spec --nodes 64 --name my-job   # the iroh-tinker launch spec
elasticgrid simulate --samples steps.json   # what the scheduler would do
```

Every command accepts `--json`.

## The scale decision

The scheduler reads a window of step samples and applies the rules in order:

1. **Never scale on thin evidence.** Fewer than `min_samples` observations
   means hold.
2. **Respect the bounds.** The target is clamped to `[min_nodes, max_nodes]`.
3. **Honour the cooldown.** After a change, wait `cooldown_s`. Elastic
   scaling without a cooldown oscillates.
4. **Queue-bound means scale up.** If 20% or more of the step was spent
   waiting for a worker, the job needs more workers.
5. **Idle means scale down.** If the step is well inside `target_step_s` and
   nobody is waiting, the current size is more than enough.
6. **Do not scale up past the deadline.** Workers that cannot finish before
   the deadline cost money and save nothing.

Every decision carries the signals it was based on, so it can be replayed and
reviewed:

```json
{
  "action": "scale_up",
  "current_nodes": 8,
  "target_nodes": 16,
  "reason": "80% of the step was spent waiting for a worker; scaling up 8 -> 16",
  "signals": {"queue_fraction": 0.8, "mean_step_s": 10.0, "...": "..."}
}
```

## The placement

`GridPlanner.plan(nodes)` returns a placement: how many nodes each provider
takes, what it costs per hour, and the ordered infra references that
iroh-tinker fails over through.

The policy is explicit:

* providers are filled cheapest first;
* `max_spot_fraction` caps the share of nodes on spot capacity;
* if the spot budget cannot hold the job, the remaining nodes are placed
  anyway, because a job that cannot start is worse than a job that breaks
  the policy;
* the placement is a pure function of (nodes, providers, policy), so the
  same plan always gives the same answer.

## Tests

```bash
pip install -e ".[dev]"
pytest
```

The suite is credential-free: the planner and the scheduler are pure
functions and are tested directly.

## Layout

```
src/elasticgrid/
  metrics.py   step samples and the rolling window of signals
  scheduler.py the scale decision (bounds, cooldown, deadline)
  grid.py      providers, placement, cost, launch spec
  cli.py       elasticgrid providers | plan | spec | simulate
```

## Licence

Apache-2.0. elasticgrid is a separate project from iroh-tinker, which stays
MIT OR Apache-2.0.
