cpu sandboxes (#10)

This commit is contained in:
Rehaan Ahmad
2026-06-14 15:39:06 -07:00
committed by GitHub
parent ce6adfe63d
commit e51aa53640
7 changed files with 94 additions and 14 deletions
Generated
+1 -1
View File
@@ -702,7 +702,7 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
[[package]]
name = "openresearch-cli"
version = "0.1.20"
version = "0.1.21"
dependencies = [
"anyhow",
"clap",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "openresearch-cli"
version = "0.1.20"
version = "0.1.21"
edition = "2021"
description = "OpenResearch CLI (orx) — Rust port"
repository = "https://github.com/alphaXiv/openresearch-cli"
+2 -2
View File
@@ -41,10 +41,10 @@ Run the tests with `cargo test`.
| `orx logout` | Removes the stored token. |
| `orx install-skills [--agent claude\|codex\|opencode\|cursor\|all]` | Installs the `orx` skill shim into local coding agents (Claude Code, Codex, OpenCode, Cursor) so they auto-discover the CLI (`orx login` also offers this). |
| `orx projects [--all]` | Lists your projects, grouped by organization. `--all` includes archived. |
| `orx compute [--gpu <id>] [--count <n>]` | Lists the GPU compute catalog, sorted by price. |
| `orx compute [--gpu <id>] [--count <n>] \| --cpu]` | Lists the GPU compute catalog, or CPU-only offers with `--cpu`, sorted by price. |
| `orx exp status <expId>` | Shows an experiment's status, branch, parent, run command, and latest run, plus a local `git diff` recipe for what the run changed. |
| `orx exp cmd <expId> [--set <command>]` | Views or sets the experiment's run command. |
| `orx exp run <expId> (--gpu <id> [--count <n>] [--disk <gb>] \| --sandbox <id>)` | Launches a run on new or existing compute. |
| `orx exp run <expId> (--gpu <id> [--count <n>] [--disk <gb>] \| --cpu <flavor> [--vcpus <n>] \| --sandbox <id>)` | Launches a run on new GPU, new CPU-only, or existing compute. |
| `orx exp cancel <expId>` | Cancels the in-flight run. |
| `orx lit "<query>" [--limit <n>] [--json]` | Full-text search alphaXiv's paper corpus (no login required). |
| `orx paper <id\|url> [--full]` | Fetch a paper's machine-readable report, or its full text with `--full`. |
+13 -5
View File
@@ -86,7 +86,7 @@ orx logout # remove the stored token
|---|---|
| `orx create-project <orgId> --name "<n>" [--repo <owner/repo>]` | Create a project **and its baseline (root node)**: bound to a GitHub repo, or on a fresh blank repo when `--repo` is omitted. See below. |
| `orx create-experiment <projectId> --title "<t>" [...]` | Add an experiment node; prints its git branch. See below. |
| `orx compute [--gpu <id>] [--count <n>]` | List the GPU compute catalog (price-sorted). See below. |
| `orx compute [--gpu <id>] [--count <n>] \| --cpu]` | List the GPU compute catalog, or CPU-only offers with `--cpu` (price-sorted). See below. |
| `orx exp status/cmd/run/cancel/wait <expId>` | Inspect, run, cancel, and wait on a single experiment node. `status` prints the node's branch, its parent's branch, the latest run's full commit SHA, and a ready-to-paste local `git diff` recipe. See below. |
| `orx exp desc <expId> [--set "<text>" \| --stdin]` | Read or overwrite the experiment's description (free-form notes). See below. |
@@ -343,7 +343,8 @@ orx create-experiment <projectId> --title "Baseline"
Each experiment node has a **run command** (the shell command that trains/evaluates
it) and is launched on **compute** you choose at run time. Compute is *not* stored
on the node — you pick a GPU (or an existing sandbox) each time you launch.
on the node — you pick a GPU, a CPU-only instance, or an existing sandbox each time
you launch.
```sh
orx exp status <expId> # status, branch, parent, run command, latest run + commit, local diff recipe
@@ -351,7 +352,9 @@ orx exp cmd <expId> # print the current run command
orx exp cmd <baseId> --set "bash run.sh" # set it ONCE on the baseline; children inherit it
orx compute # browse GPU offers (price-sorted)
orx compute --gpu H100_SXM --count 1 # filter the catalog
orx exp run <expId> --gpu H100_SXM --count 1 [--disk 100] # launch on a NEW instance
orx compute --cpu # browse CPU-only offers (price-sorted)
orx exp run <expId> --gpu H100_SXM --count 1 [--disk 100] # launch on a NEW GPU instance
orx exp run <expId> --cpu cpu5c --vcpus 8 # launch on a NEW CPU-only instance
orx exp run <expId> --sandbox <sandboxId> # launch on an EXISTING node
orx exp cancel <expId> # cancel the in-flight run
```
@@ -370,14 +373,19 @@ Rules and notes:
safety net, `orx exp run` refuses a child whose branch has **no changes over its
parent** (the tell-tale of "queued before pushing") — push and retry, or pass
`--force` to run the unchanged code deliberately.
- **Pick compute with exactly one of `--gpu` or `--sandbox`.** With `--gpu`,
`--count` defaults to `1` and `--disk` to `100` (GB). New instances are
- **Pick compute with exactly one of `--gpu`, `--cpu`, or `--sandbox`.** With
`--gpu`, `--count` defaults to `1` and `--disk` to `100` (GB). New instances are
**RunPod-only** — the server picks the cheapest matching RunPod offer for the
chosen (gpu, count); browse valid gpu ids and prices with `orx compute`.
- **GPU ids are exact enum strings, not family names.** `--gpu H100` is invalid —
the variant suffix is part of the id (`H100_SXM`, `H100_PCIE`, `A100_SXM_80GB`,
`RTX_4090`, …). Use the exact `GPU` column value from `orx compute`; run it
first if unsure.
- **Use `--cpu` for GPU-less work** (data prep, eval harnesses, CPU-bound papers).
The flavor is one of `cpu5c` (compute), `cpu5g` (general), or `cpu5m` (memory);
`--vcpus` is one of `2`, `8`, `32` (default `8`). Browse offers with
`orx compute --cpu`. CPU instances size their disk from the vCPU count, so there
is no `--disk` knob.
- `orx exp run` **queues** the run and returns immediately — it does not wait.
Follow progress with `orx runs <projectId>` and `orx logs <runId>`, or block
with `orx exp wait` (below).
+28
View File
@@ -111,6 +111,30 @@ pub struct ListCatalog {
pub offers: Vec<GpuOffer>,
}
/// A single CPU-only offer from the CPU catalog (`GET /compute/catalog/cpu`).
/// Sibling to [`GpuOffer`]; CPU instances live in their own RunPod-only catalog.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CpuOffer {
pub provider: String,
pub offer_id: String,
/// Flavor id: cpu5c (compute), cpu5g (general), or cpu5m (memory-optimized).
pub cpu_flavor: String,
/// Virtual CPUs allocated to the instance.
pub vcpus: f64,
/// System RAM in GB.
pub ram_gb: f64,
pub price_per_hour: f64,
/// Disk storage rate while running, USD per GB per hour.
pub disk_per_gb_hour: f64,
pub region: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListCpuCatalog {
pub offers: Vec<CpuOffer>,
}
/// Response of `GET /experiments/{id}`: the experiment plus its most recent run.
#[derive(Debug, Clone, Deserialize)]
#[serde(rename_all = "camelCase")]
@@ -707,6 +731,10 @@ pub async fn list_catalog(creds: &Credentials) -> Result<ListCatalog> {
api_get(creds, "/compute/catalog").await
}
pub async fn list_cpu_catalog(creds: &Credentials) -> Result<ListCpuCatalog> {
api_get(creds, "/compute/catalog/cpu").await
}
pub async fn get_experiment(creds: &Credentials, exp_id: &str) -> Result<GetExperimentResult> {
api_get(creds, &format!("/experiments/{}", exp_id)).await
}
+43 -3
View File
@@ -1,17 +1,25 @@
//! The `compute` command. Lists the GPU offer catalog as a price-sorted table.
//! The `compute` command. Lists the GPU offer catalog as a price-sorted table,
//! or the CPU-only catalog with `--cpu`.
//!
//! The catalog endpoint spans every provider, but experiment runs launched on a
//! *new* instance (`orx exp run --gpu ...`) are RunPod-only — the server
//! resolves the cheapest matching RunPod offer for the chosen (gpu, count). So
//! this command filters to RunPod offers: everything it lists is launchable.
//! CPU offers are RunPod-only to begin with, so no filtering is needed there.
use crate::client::list_catalog;
use crate::client::{list_catalog, list_cpu_catalog};
use crate::error::{require_credentials, Result};
use crate::output::print_table;
/// Lists the GPU compute catalog, optionally filtered by gpu id and/or count.
/// Lists the compute catalog. With `--cpu`, lists CPU-only offers; otherwise the
/// GPU catalog, optionally filtered by gpu id and/or count.
pub async fn run(args: crate::ComputeArgs) -> Result<()> {
let creds = require_credentials().await;
if args.cpu {
return run_cpu(&creds).await;
}
let offers = list_catalog(&creds).await?.offers;
// The API already returns offers sorted by price ascending; keep that order.
@@ -56,3 +64,35 @@ pub async fn run(args: crate::ComputeArgs) -> Result<()> {
Ok(())
}
/// Lists the CPU-only offer catalog as a price-sorted table. Launch one with
/// `orx exp run --cpu <flavor> --vcpus <n>`.
async fn run_cpu(creds: &crate::config::Credentials) -> Result<()> {
let offers = list_cpu_catalog(creds).await?.offers;
if offers.is_empty() {
println!("No CPU compute offers available.");
return Ok(());
}
let rows: Vec<Vec<String>> = offers
.iter()
.map(|o| {
vec![
o.cpu_flavor.clone(),
format!("{:.0}", o.vcpus),
format!("${:.2}", o.price_per_hour),
format!("{:.0}", o.ram_gb),
o.region.clone().unwrap_or_else(|| "—".to_string()),
o.provider.clone(),
]
})
.collect();
print_table(
&["FLAVOR", "VCPUS", "$/HR", "RAM(GB)", "REGION", "PROVIDER"],
&rows,
);
Ok(())
}
+6 -2
View File
@@ -248,10 +248,14 @@ pub struct CreateExperimentArgs {
#[derive(Args, Debug)]
pub struct ComputeArgs {
/// Filter to one GPU id (e.g. `H100_SXM`). Case-insensitive.
/// List CPU-only instance offers instead of the GPU catalog. CPU instances
/// suit GPU-less experiments (data prep, eval harnesses, CPU-bound papers).
#[arg(long)]
pub cpu: bool,
/// Filter to one GPU id (e.g. `H100_SXM`). Case-insensitive. GPU mode only.
#[arg(long)]
pub gpu: Option<String>,
/// Filter to a specific GPU count per instance.
/// Filter to a specific GPU count per instance. GPU mode only.
#[arg(long)]
pub count: Option<i64>,
}