Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .github/uv-sglang-cpu.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[[index]]
name = "torch"
url = "https://download.pytorch.org/whl/cpu"

[[index]]
name = "torchvision"
url = "https://download.pytorch.org/whl/cpu"

[[index]]
name = "torchaudio"
url = "https://download.pytorch.org/whl/cpu"

[[index]]
name = "triton"
url = "https://download.pytorch.org/whl/cpu"
120 changes: 120 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
name: CI

env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: "true"

on:
push:
pull_request:

jobs:
cpu:
name: CPU checks
runs-on: ubuntu-latest
steps:
- name: Check out repo
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.13

- name: Install dependencies
run: uv sync --group dev --group sglang

- name: Verify Python
run: uv run python --version

- name: Run tests
run: uv run pytest

- name: Run simulation smoke
run: uv run aether simulate --config experiments/greensserve/baseline.yaml --out /tmp/aether-sim.csv

- name: Run mock measurement smoke
run: uv run aether launch --backend mock --config experiments/greensserve/mock_measurement.yaml --out /tmp/aether-mock

- name: Verify SGLang submodule pin
run: |
test "$(git -C third_party/sglang rev-parse HEAD)" = "127b9e3283f7c2a43234b852ff5c9f1796d53624"

- name: Verify SGLang patch applies
run: |
git -C third_party/sglang apply --check ../../patches/sglang/v0.5.12/0001-aether-ipw-metrics-scaffold.patch

sglang-cpu:
name: SGLang CPU launch
runs-on: ubuntu-24.04
timeout-minutes: 60
env:
UV_CONFIG_FILE: ${{ github.workspace }}/.github/uv-sglang-cpu.toml
SGLANG_USE_CPU_ENGINE: "1"
steps:
- name: Check out repo
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install uv
uses: astral-sh/setup-uv@v6
with:
enable-cache: true

- name: Set up Python
run: uv python install 3.13

- name: Install system packages for SGLang CPU
run: |
sudo apt-get update
sudo apt-get install --no-install-recommends -y google-perftools libtbb-dev libnuma-dev numactl

- name: Install Aether dependencies
run: uv sync --group dev --group sglang

- name: Apply Aether SGLang metrics patch
run: |
git -C third_party/sglang apply ../../patches/sglang/v0.5.12/0001-aether-ipw-metrics-scaffold.patch
test -f third_party/sglang/AETHER_IPW_METRICS.md

- name: Install SGLang CPU from pinned submodule
run: |
cp third_party/sglang/python/pyproject_cpu.toml third_party/sglang/python/pyproject.toml
cp third_party/sglang/sgl-kernel/pyproject_cpu.toml third_party/sglang/sgl-kernel/pyproject.toml
uv pip install --upgrade pip setuptools wheel
uv pip install third_party/sglang/python
uv pip install third_party/sglang/sgl-kernel
uv run python -c "import sglang, sgl_kernel; print('sglang import ok', sglang.__file__); print('sgl_kernel import ok', sgl_kernel.__file__)"

- name: Run Aether against real SGLang CPU server
env:
LD_LIBRARY_PATH: /usr/lib/x86_64-linux-gnu
LD_PRELOAD: ${{ github.workspace }}/.venv/lib/libiomp5.so:/usr/lib/x86_64-linux-gnu/libtcmalloc.so.4:/usr/lib/x86_64-linux-gnu/libtbbmalloc.so.2
run: |
uv run aether doctor --config experiments/greensserve/sglang_cpu_ci.yaml
timeout 15m uv run aether launch --backend sglang --config experiments/greensserve/sglang_cpu_ci.yaml --out /tmp/aether-sglang-cpu

- name: Show Aether SGLang CPU outputs
if: always()
run: |
echo "===== command.json ====="
test -f /tmp/aether-sglang-cpu/command.json && cat /tmp/aether-sglang-cpu/command.json || true
echo "===== summary.csv ====="
test -f /tmp/aether-sglang-cpu/summary.csv && cat /tmp/aether-sglang-cpu/summary.csv || true
echo "===== events.jsonl ====="
test -f /tmp/aether-sglang-cpu/events.jsonl && head -100 /tmp/aether-sglang-cpu/events.jsonl || true
echo "===== sglang.log tail ====="
test -f /tmp/aether-sglang-cpu/sglang.log && tail -200 /tmp/aether-sglang-cpu/sglang.log || true

- name: Upload SGLang CPU artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: aether-sglang-cpu
path: /tmp/aether-sglang-cpu
if-no-files-found: ignore
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ ipython_config.py
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# uv.lock
.uv-cache/

# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
Expand Down Expand Up @@ -206,6 +207,9 @@ tempCodeRunnerFile.py
# Ruff stuff:
.ruff_cache/

# Aether experiment outputs
results/

# PyPI configuration file
.pypirc

Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "third_party/sglang"]
path = third_party/sglang
url = https://github.com/sgl-project/sglang.git
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.13
59 changes: 59 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# AGENTS.md

## Project

Aether is a Python package and CLI for GreenServe intelligence-per-watt (IPW)
simulation and measurement. Use uv and Python 3.13. Keep the package installable with:

```bash
uv python install 3.13
uv sync --group dev
```

Run the CPU-safe checks with:

```bash
uv run pytest
uv run aether simulate --config experiments/greensserve/baseline.yaml --out /tmp/aether-sim.csv
uv run aether launch --backend mock --config experiments/greensserve/mock_measurement.yaml --out /tmp/aether-mock
git -C third_party/sglang apply --check ../../patches/sglang/v0.5.12/0001-aether-ipw-metrics-scaffold.patch
```

GitHub CI also runs a real SGLang CPU integration job. It installs SGLang from
the pinned submodule using SGLang's `pyproject_cpu.toml`, applies the Aether
metrics patch, launches `python -m sglang.launch_server --device cpu`, sends a
tiny `/generate` request through `aether launch --backend sglang`, and prints
`summary.csv`, `events.jsonl`, and `sglang.log`.

## Network Proxy Preference

When a download or dependency fetch appears stuck or blocked by network
restrictions, retry with these proxy variables before giving up:

```bash
http_proxy=http://127.0.0.1:10808
https_proxy=http://127.0.0.1:10808
all_proxy=socks5://127.0.0.1:10808
HTTP_PROXY=http://127.0.0.1:10808
HTTPS_PROXY=http://127.0.0.1:10808
ALL_PROXY=socks5://127.0.0.1:10808
```

## Repo Rules

- Keep real SGLang/GPU measurement code optional. Importing `aether` and running
unit tests must not require CUDA, NVML, SGLang, or a GPU. CI may install
SGLang separately for the dedicated CPU integration job.
- Use mock measurement as the correctness gate for launcher, collector, and
result-writer behavior.
- Keep normalized CSV/JSONL schemas shared across simulation, mock, and real
measurement paths.
- Store experiment outputs under `results/`; this directory is ignored.
- Keep SGLang work under `third_party/sglang` and patch files under
`patches/sglang/v0.5.12/`.
- SGLang patches in this bootstrap are metrics-only. Do not add scheduler,
recompute/swap, or DVFS behavior changes without a new design doc.
- SGLang `v0.5.12` does not fully install on local macOS arm64 in this
workspace because `sgl-deep-gemm==0.1.0` provides Linux wheels only. Use
macOS for patch-apply and Aether mock validation; use GitHub Actions or Linux
hosts for SGLang CPU serving tests, and Linux GPU hosts for NVML/GPU runs.
101 changes: 101 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Aether

Aether is an installable Python CLI for GreenServe-style intelligence-per-watt
(IPW) experiments. The current codebase is intentionally runnable on CPU: it
supports pure simulation and a deterministic mock measurement backend that
validates the same result schema used by future SGLang/GPU runs.

The GitHub CI is CPU-only. It uses uv with Python 3.13, runs tests and smoke
commands, verifies the SGLang metrics patch applies to the pinned submodule,
and has a dedicated job that installs SGLang's CPU source build, launches an
actual SGLang CPU server, sends an Aether-managed request, and prints the
normalized outputs.

Real GPU experiments are kept separate. The SGLang backend is present, but GPU
power sampling only runs when SGLang, CUDA/NVML, and a workload are available
in the active environment.

Note: SGLang `v0.5.12` real serving is Linux-oriented. On this local macOS arm64
machine, full install is blocked by upstream Linux-only `sgl-deep-gemm` wheels;
see `docs/real-data-collection.md` for the smoke-test result and the Linux CPU
CI path.

## Install

```bash
uv python install 3.13
uv sync --group dev
```

After install:

```bash
uv run aether doctor --config experiments/greensserve/baseline.yaml
uv run aether simulate --config experiments/greensserve/baseline.yaml --out results/sim.csv
uv run aether launch --backend mock --config experiments/greensserve/mock_measurement.yaml --out results/mock-run
uv run pytest
```

When published, the intended user install is:

```bash
pip install aether
# or, with uv:
uv tool install aether
```

## CLI

```bash
uv run aether simulate --config CONFIG.yaml --out results/sim.csv
uv run aether launch --backend mock --config CONFIG.yaml --out results/mock-run
uv run aether launch --backend sglang --config CONFIG.yaml --out results/real-run
uv run aether doctor --config CONFIG.yaml
uv run aether explain --config CONFIG.yaml
```

`simulate` expands YAML sweeps and writes one normalized CSV row per scenario.
`launch --backend mock` runs a deterministic CPU-only measurement fixture and
writes `summary.csv` plus `events.jsonl`. `launch --backend sglang` starts
`python -m sglang.launch_server` with the configured SGLang arguments, then
collects normalized measurement output if the real environment is available.

## Config

Examples live in `experiments/greensserve/`.

- `baseline.yaml`: pure simulation sweep.
- `mock_measurement.yaml`: CPU-only launcher and collector validation.
- `sglang_cpu_ci.yaml`: real SGLang CPU launch used by GitHub Actions.
- `sglang_real.yaml`: template for future SGLang/GPU measurement.

The SGLang config accepts structured args:

```yaml
sglang:
args:
model-path: meta-llama/Llama-3.1-8B-Instruct
host: 127.0.0.1
port: 30000
context-length: 8192
enable-metrics: true
extra_args:
- --trust-remote-code
```

Booleans set to `true` are rendered as flags; `false` and `null` are omitted.
All other values are rendered as `--flag value`.

## Real Data Collection

Real collection is documented in `docs/real-data-collection.md`. In short:

1. Initialize the SGLang submodule pinned to `v0.5.12`.
2. Apply the metrics-only patch series from `patches/sglang/v0.5.12/`.
3. Install patched SGLang in the active GPU environment.
4. Run `uv run aether launch --backend sglang --config experiments/greensserve/sglang_real.yaml --out results/real-run`.
5. Use `summary.csv`, `events.jsonl`, SGLang logs, and NVML samples for analysis.

The unit test suite does not require SGLang, CUDA, NVML, or a GPU. GitHub CI
adds an integration job that installs SGLang CPU separately and exercises the
same Aether result writer against a live local SGLang server.
43 changes: 43 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Aether Architecture

Aether has three layers:

1. Config and CLI: load YAML, render SGLang args, run commands, and write
normalized outputs.
2. Simulation: compute GreenServe/IPW metrics from reconstructed formulas and
scenario sweeps.
3. Measurement: collect normalized data from either deterministic CPU mock
fixtures or future real SGLang/GPU runs.

Real measurement is isolated from simulation and mock validation. The SGLang
backend imports optional dependencies only inside the backend code path and
fails with setup guidance if SGLang, CUDA, or NVML are unavailable.

## Data Flow

```text
YAML config
-> config loader
-> simulate: sweep expander -> formula engine -> summary rows -> CSV
-> launch mock: synthetic events -> normalized summary/events -> CSV/JSONL
-> launch sglang: process launcher + samplers -> normalized summary/events
```

## Normalized Outputs

All execution paths write the same summary fields where possible:

- scenario identity: experiment, backend, scenario_id
- configuration: model, hardware, context length, compression ratio, routing
policy, scheduling policy
- capacity: KV bytes per token, effective KV bytes, max in-flight sequences
- timing: prefill seconds, decode seconds, elapsed seconds, TTFT/TBT estimates
- energy: joules, average watts, tokens/sec, tokens/watt, tokens/joule
- scheduling: swap bytes, recompute tokens, selected scheduling action
- quality: quality score and quality-normalized IPW

## SGLang Integration

The submodule is pinned to SGLang `v0.5.12`. The first patch series is
observability-only: it adds or documents hooks for power, token, KV, swap, and
preemption metrics without changing scheduling behavior.
Loading
Loading