Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
96 commits
Select commit Hold shift + click to select a range
b1ebf17
Add backend-agnostic lease ledger (leasing/controller redesign)
Erotemic Jun 16, 2026
1274470
Add serving catalog parser to the leasing model
Erotemic Jun 16, 2026
b512c62
Add backend protocol + controller (reconcile loop)
Erotemic Jun 16, 2026
0100a52
Add leasing CLI verbs + endpoint env-file (dry-run backend)
Erotemic Jun 16, 2026
3f136a3
Add single-host GPU placement planner (Compose backend, slice 1)
Erotemic Jun 16, 2026
301d4de
Add Compose backend (converge-style) + controller converge path
Erotemic Jun 16, 2026
aca9256
Add Compose LiteLLM front door, real descriptor base_url, converge lock
Erotemic Jun 16, 2026
9c8e05a
Consolidate GPU placement primitives into hardware.py
Erotemic Jun 16, 2026
7e55df7
Consolidate HTTP readiness probes into infer_stack.probe
Erotemic Jun 16, 2026
77db567
Add Ollama pull/warmup readiness to the Compose backend
Erotemic Jun 17, 2026
0f7c665
Fix Compose GPU capabilities shape; add yardrat test plan
Erotemic Jun 17, 2026
630c5df
Guard rendered compose with docker compose config validation
Erotemic Jun 17, 2026
29234e5
Fix test isolation: force INFER_STACK_* in subprocess run_cli helpers
Erotemic Jun 17, 2026
9416bf9
Make Compose observe() tolerant of a stale/invalid compose file
Erotemic Jun 17, 2026
27f08ad
Fix corrupted catalog heredoc in the leasing test plan
Erotemic Jun 17, 2026
5565624
Manage the LiteLLM key in-backend; ship it in the env-file; drop bad …
Erotemic Jun 17, 2026
a1456b6
Make `config paths`/`status` relevant to the leasing model
Erotemic Jun 17, 2026
2955c74
Test plan: add Content-Type to chat curls (F10, doc-only)
Erotemic Jun 17, 2026
ad1c96e
Make day-2 compose wrappers leasing-aware (logs/ps/restart/pull/start…
Erotemic Jun 17, 2026
a29eb51
Add dev/e2e_tests: executable leasing e2e harness with report
Erotemic Jun 17, 2026
cb7a255
Fix e2e harness: exit (not return) in GPU-tier skip path
Erotemic Jun 17, 2026
de2d4f7
Add --include-display-gpus (close F5), both-GPU e2e tier, and demo wa…
Erotemic Jun 17, 2026
6efad9b
Fix LiteLLM not reloading on config change (coalescing readiness bug)
Erotemic Jun 17, 2026
19e461a
e2e harness: write report+rsync on Ctrl-C, exclude heavy caches from …
Erotemic Jun 17, 2026
56d24b3
Track LiteLLM keep-alive follow-up (#1 static table chosen, #2 DB ret…
Erotemic Jun 17, 2026
b030487
e2e: print rsync at start + reset GPU state between tiers; record des…
Erotemic Jun 17, 2026
b41405f
Record resolved keep-warm eviction policy + approve compat-key group ids
Erotemic Jun 17, 2026
2abdd3d
e2e: speed up GPU tiers (enforce-eager, SmolLM2, trim intentional waits)
Erotemic Jun 17, 2026
f47fb5b
Fix converge crash when releasing the last model (empty desired set)
Erotemic Jun 17, 2026
118187c
e2e: wipe ledger between tiers, add progress heartbeat + cleanup.sh
Erotemic Jun 17, 2026
22e703e
e2e: rename endpoints qwen-*->smol-*, isolate config dir, fix status …
Erotemic Jun 18, 2026
ab73b77
e2e: robustly clean up the progress heartbeat (no stragglers after a …
Erotemic Jun 18, 2026
253a361
docs: switch leasing demo to SmolLM2; add CLI-redesign proposal
Erotemic Jun 18, 2026
70af3cf
cli phase 1: `catalog` editor submodal + `help tree`
Erotemic Jun 18, 2026
562dd13
cli phase 2: `config` submodal + durable settings (backend, data_dir)
Erotemic Jun 18, 2026
b3433b2
cli phase 3: `secret` submodal (get/set/list); keep `secrets` alias
Erotemic Jun 18, 2026
348c9aa
cli phase 4: `stack` submodal for day-2 ops; logs/ps kept top-level
Erotemic Jun 18, 2026
d27d139
cli phase 5: group the pre-leasing profile world under `infer-stack l…
Erotemic Jun 18, 2026
c7f24f8
cli phase 6: docs for the reorg (demo on new CLI, README banner, chan…
Erotemic Jun 18, 2026
6e8d419
cli: render `help tree` with rich (pretty + alignment-proof)
Erotemic Jun 18, 2026
3d30ce2
cli: interactive `config init` (rich prompts + confirm) with --yes
Erotemic Jun 18, 2026
8f70e61
cli: syntax-highlight catalog/config YAML output (rich)
Erotemic Jun 18, 2026
5a82963
cli: add `python -m infer_stack` entry + lazy heavy imports for snapp…
Erotemic Jun 18, 2026
e2e8a29
leasing: managed Open WebUI (default on), `infer-stack test`, `env` r…
Erotemic Jun 18, 2026
fc3e79d
leasing: fold `secret` into `env` (one verb for the managed env-file)
Erotemic Jun 18, 2026
f24f1e6
cli: make `infer-stack status` leasing-aware (no stale "run setup" nag)
Erotemic Jun 18, 2026
b77a608
cli: rich-format `infer-stack status` on a terminal
Erotemic Jun 18, 2026
b31d17e
catalog: default endpoint NAME to the model (Open WebUI shows the model)
Erotemic Jun 18, 2026
a631cba
leasing: release --all, rich leases, compose diff-approval, loguru na…
Erotemic Jun 18, 2026
70d9cc3
leasing: force-evict released (idle) models — `evict` + `release --ev…
Erotemic Jun 18, 2026
61df5fd
catalog: auto-increment default endpoint names (-N) + multi-rm; model…
Erotemic Jun 18, 2026
5798e68
docs(leasing-demo): show explicit endpoint-name setup alongside model…
Erotemic Jun 18, 2026
2340fe3
leasing: add `infer-stack wait` (block until ready) for serve --no-wa…
Erotemic Jun 18, 2026
fe90d2e
leasing: render/apply split, fail-fast placement, legible naming + st…
Erotemic Jun 19, 2026
952c298
leasing: gate every converge behind the compose-diff prompt
Erotemic Jun 19, 2026
9802179
leasing: model-vs-endpoint hint in the unknown-endpoint error
Erotemic Jun 19, 2026
b034131
leasing: use every GPU by default; skipping the display GPU is opt-in
Erotemic Jun 19, 2026
e58b846
cli: config init mode message + --fresh; `<section> show` lists all
Erotemic Jun 19, 2026
631d8d9
config init: prompt for every known setting (incl. skip_display_gpus,…
Erotemic Jun 19, 2026
d5e39d7
leasing: keep the front door up at zero models (release/evict ≠ full …
Erotemic Jun 19, 2026
98b520f
leasing: optional single-port HTTP reverse proxy (front door)
Erotemic Jun 19, 2026
ddc243a
leasing: optional Textual TUI to monitor + control the stack
Erotemic Jun 19, 2026
e888eff
tui: multi-pane dashboard — catalog, leases/groups, live switchable logs
Erotemic Jun 19, 2026
4d90c54
tui: calmer, roomier layout — soft borders, padding, resizable panes
Erotemic Jun 19, 2026
4ef5147
catalog: seed from server introspection (suggestion pool)
Erotemic Jun 19, 2026
2313adc
TUI tweaks
Erotemic Jun 19, 2026
a4f35be
catalog: bring current-gen families into the suggestion pool
Erotemic Jun 19, 2026
889f10e
tui tweaks
Erotemic Jun 19, 2026
1c9979a
tui work
Erotemic Jun 19, 2026
059c2cd
tui work
Erotemic Jun 19, 2026
08353d3
tui: make lease↔deployment relationship legible; compact buttons
Erotemic Jun 19, 2026
0ab64db
leasing: rename session_id -> lease_id (hard, breaking)
Erotemic Jun 19, 2026
4930d02
journal: lease↔deployment legibility + session→lease rename
Erotemic Jun 19, 2026
2c2d815
leasing: rename 'group' concept -> 'deployment' (hard, breaking)
Erotemic Jun 19, 2026
508f442
cleanup: remove dead placeholder tuning.py (no importers)
Erotemic Jun 19, 2026
1677e99
cli: remove pre-leasing profile world; make status holistic
Erotemic Jun 19, 2026
05f4ff1
cli: remove resolver/validator/cli-compose/cli-probes; carve context
Erotemic Jun 19, 2026
ad54818
backends: drop superseded compose_renderer (keep kubeai concept)
Erotemic Jun 19, 2026
6be217a
tui: advanced endpoint params + edit/remove for endpoints & models
Erotemic Jun 19, 2026
1f5c329
tui: top-level Dashboard/Settings tabs + editable settings
Erotemic Jun 19, 2026
002f16e
tui: docker Control tab (compose up/down + compose-file path)
Erotemic Jun 19, 2026
257da4c
tui: model cache/quant columns (cheap existence check)
Erotemic Jun 19, 2026
b4947e5
tui: drag bars between endpoints|models and leases|deployments
Erotemic Jun 19, 2026
7c5a1e6
tui: labeled engine-adaptive endpoint wizard + layout fixes
Erotemic Jun 19, 2026
e599e1f
tui: make leases + deployments panes collapsible
Erotemic Jun 19, 2026
75c54c1
config: drop dead catalog chain; delete old top-level catalog.py
Erotemic Jun 19, 2026
383ed20
tui: fix endpoint action buttons overflowing the narrow sidebar
Erotemic Jun 19, 2026
92e8880
tui: richer API tab (URLs, list-models, curl) + clipboard copy
Erotemic Jun 19, 2026
a48c1d5
tui: fix API-tab MarkupError on URL render (use plain text)
Erotemic Jun 19, 2026
ae0af53
suggest: never size gpu_memory_utilization below the pool default
Erotemic Jun 19, 2026
80b4641
tui: fix System pane never polling + robust clipboard copy
Erotemic Jun 19, 2026
363d5bd
leasing: make LiteLLM optional + let Open WebUI manage Ollama's own m…
Erotemic Jun 19, 2026
679f75e
docs: add a Sphinx user manual; move the current guides into it
Erotemic Jun 19, 2026
45611d6
e2e+fix: cover optional-litellm / direct-ollama-webui / GPU pinning o…
Erotemic Jun 20, 2026
0344636
e2e: fix dead --include-display-gpus flag + bad status assertion in n…
Erotemic Jun 20, 2026
8150e33
Fix TUI resize bug and collapse serve and acquire + other stuff
edlwang Jun 23, 2026
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,6 @@ generated/kubeai/*
!examples/single-node/
!examples/single-node/config.yaml
!examples/single-node/models.yaml

# rsync target for e2e harness reports (developer artifacts, not source)
e2e-report-*/
548 changes: 548 additions & 0 deletions CHANGELOG.md

Large diffs are not rendered by default.

9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
[![Python versions](https://img.shields.io/pypi/pyversions/infer-stack.svg)](https://pypi.org/project/infer-stack/)
[![License](https://img.shields.io/pypi/l/infer-stack.svg)](https://github.com/AIQ-Kitware/infer_stack/blob/main/LICENSE)

> **Heads up — the leasing model is now the primary workflow.** Declare models
> in a catalog (`infer-stack catalog …`) and `acquire`/`run` endpoints
> on demand; see `docs/source/manual/` (the Ollama + Open WebUI tutorial and the
> leasing demo) and `infer-stack help tree`. The
> **named stack profiles** documented below (`setup`/`render`/`up`/`switch`/…)
> are the pre-leasing model and now live under **`infer-stack legacy <command>`**
> (e.g. `infer-stack legacy render`). This README still describes that legacy
> flow; a leasing-oriented rewrite is pending.

`infer_stack` manages **named stack profiles** for local and Kubernetes-backed inference.

A stack profile is a small graph made from:
Expand Down
178 changes: 178 additions & 0 deletions dev/cli-redesign.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
# infer-stack CLI redesign — submodals, a catalog editor, and `help tree`

Status: **implemented** (decisions: noun-verb grammar, full phased reorg).
Shipped across phases: `catalog` editor + `help tree`; `config` submodal +
settings store (honors `backend`/`data_dir`); `secret` get/set/list; `stack`
day-2 group; the pre-leasing profile world grouped under `legacy`. Top-level is
now the leasing loop + submodals. Companion to the leasing redesign
(`dev/leasing-followups.md`, `dev/infer-stack-redesign-critique.md`).

The tree and rationale below are kept as the design record; `infer-stack help
tree` prints the live surface.

## What we have

`infer-stack` is one flat `scfg.ModalCLI` (`infer_stack/cli/__init__.py`) with
~38 top-level subcommands spanning three eras mashed together:

- **Leasing (the new primary model):** `acquire release renew run serve leases
secrets`
- **Day-2 ops on the running stack:** `logs ps restart pull start stop`
- **Meta:** `version config(+paths) paths status env`
- **Legacy profile/active-profile world:** `setup init resolve validate lock
render switch list-models list-profiles explain describe-profile
verify-profile kubeai-sync-resource-profiles up down purge deploy diagnose
wait-ready smoke-test benchmark ollama-pull ollama-list ollama-ps`

Problems:
- **Sprawl** — 38 flat verbs; the hot path (acquire/run) is buried among
legacy profile machinery.
- **No ergonomic catalog editing** — adding a model/endpoint means hand-editing
`~/.config/infer_stack/catalog.yaml`. (The demo literally `cat >` a heredoc.)
- **`init` is mis-placed** — it's config bootstrap, not a top-level verb.
- **No way to see the whole surface** — nested help requires walking each group.

Good news: `config` is already a nested `ModalCLI` (`config paths`), and
scriptconfig 0.9.1 supports **3-level** nesting (verified: `infer-stack catalog
model add NAME` works, and group-level `--help` renders). So everything below is
feasible with the framework we already use.

## Principles

1. **Hot paths stay at the top level.** The kwdagger/AIQ pipeline-node loop —
`acquire`, `run`, `release`, `leases`, `status` — and the two most
common day-2 verbs (`logs`, `ps`) remain one token deep.
2. **Everything else is grouped into noun submodals** (`catalog`, `config`,
`secret`, `stack`) so the surface reads as a small set of areas.
3. **`legacy` is a holding pen,** not a graveyard: the pre-leasing profile world
moves under `infer-stack legacy …` verbatim. We promote commands out as we
give them leasing-native behavior, and delete `legacy` wholesale once empty.
4. **`help tree` shows the totality** (ported from `aivm help tree`).

## Proposed CLI tree

```
infer-stack
│ # ── hot path: the leasing loop (top-level) ──
├── acquire <ep…> lease endpoints, block until ready
├── run -- <cmd> acquire, run cmd with endpoint env, release
├── serve <ep…> standing service (infinite lease)
├── release <id|--env-file> release a lease
├── renew <id> extend a lease's TTL
├── leases show leases + deployment groups
├── status overall stack status (+ leasing summary)
├── logs / ps day-2 convenience aliases (-> stack logs/ps)
├── secrets [KEY] convenience alias (-> secret get)
├── version
├── help [cmd…] | help tree per-command help; `tree` prints the whole surface
│
│ # ── catalog: edit the user catalog without raw YAML (NEW) ──
├── catalog
│ ├── init write a starter catalog.yaml
│ ├── path print the catalog path
│ ├── show [name] pretty-print the catalog (or one entry)
│ ├── validate parse + cross-reference check
│ ├── edit open $EDITOR (escape hatch)
│ ├── model add|list|show|rm models: source/revision/quantization
│ ├── endpoint add|list|show|rm endpoints: engine/model/runtime/reclaim
│ ├── host add|list|rm runtime_hosts: ollama daemons / placement
│ └── bundle add|list|rm bundles: named endpoint groups
│
│ # ── config: where things live + durable settings ──
├── config
│ ├── init was top-level `init`
│ ├── paths (existing) resolved file/dir locations
│ ├── show effective config
│ ├── set <key> <value> persist defaults — e.g. backend=compose,
│ │ data_dir=/data/infer-stack (kills two smells)
│ └── edit open $EDITOR
│
│ # ── secret: the managed LiteLLM key + friends ──
├── secret
│ ├── get [KEY] was `secrets`; $(infer-stack secret get …)
│ ├── set KEY=VALUE e.g. HF_TOKEN -> the managed .env
│ └── list export-style lines
│
│ # ── stack: day-2 ops on the running (leased) compose stack ──
├── stack
│ ├── logs ├── ps ├── restart ├── start ├── stop ├── pull └── down
│
│ # ── legacy: pre-leasing profile/active-profile world (promote out, then delete) ──
└── legacy
├── setup ├── render ├── switch ├── resolve ├── lock ├── validate
├── list-models ├── list-profiles ├── explain ├── describe-profile
├── verify-profile ├── kubeai-sync-resource-profiles
├── up ├── down ├── purge ├── deploy
├── env ├── diagnose ├── wait-ready ├── smoke-test ├── benchmark
└── ollama-pull ├── ollama-list └── ollama-ps
```

### The `catalog` submodal (the headline new feature)

A `CatalogEditor` loads the YAML, mutates the dict, validates via
`Catalog.from_dict`, and writes it back (atomic temp+rename). Flags map 1:1 to
the schema so the demo's heredoc becomes:

```bash
infer-stack catalog init
infer-stack catalog model add smol17b --source hf://HuggingFaceTB/SmolLM2-1.7B-Instruct
infer-stack catalog endpoint add chat --engine vllm --model smol17b \
--max-model-len 8192 --gpu-mem 0.4 --extra-arg --dtype=half \
--reclaim keep-warm
infer-stack catalog endpoint add chat-fast --engine vllm --model smol135 --reclaim stop
infer-stack catalog host add local-ollama --engine ollama --gpu 0 --keep-alive 5m
infer-stack catalog bundle add pair chat chat-fast
infer-stack catalog validate
```

Notes / decisions for this group:
- **Round-tripping comments:** plain `yaml` drops comments. For a
tool-generated catalog that's acceptable; if we want to preserve hand-written
comments, use `ruamel.yaml`. Proposed: start with plain yaml, revisit.
- **`add` is idempotent-ish:** `add` errors if the name exists unless
`--force`/`--update`; `rm` is by name with a friendly "not found".
- **`--dry-run`** prints the resulting YAML without writing.
- Validation reuses the existing `Catalog` parser, so the editor can never write
a catalog the leasing path would reject.

### `help tree`

Walk `ManageCLI`'s registered subconfigs recursively (each is a `DataConfig` or
nested `ModalCLI`), printing an indented tree with the one-line description from
each class docstring. `infer-stack help <cmd…>` dispatches to that command's
`--help`. Pure introspection over the modal registry — no per-command wiring.

## Migration plan (phased; no hard back-compat required)

The user has said back-compat isn't required, but we minimize churn to the
already-green e2e harness + demo, which only use top-level
`acquire/run/serve/release/renew/leases/secrets/status/logs/ps/paths`.

1. **Add `catalog` + `help tree`** (purely additive). Unblocks the demo's
"add a model without YAML" and gives the totality view. Lowest risk.
2. **Add `config` subcommands** (`init`→`config init` with a thin top-level
`init` shim that warns; `config set/show/edit`). Wire `config set` to a real
settings store so `--backend compose` / `data_dir` stop being repeated
(leasing-followups smells #1/#2).
3. **Add `secret` group** (`secret get/set/list`); keep `secrets` as a top-level
alias for `secret get` (script compatibility for `$(infer-stack secrets …)`).
4. **Add `stack` group**; alias `logs`/`ps` at top level; the other day-2 verbs
move under `stack`.
5. **Introduce `legacy`**; move the profile-world commands under it. Keep
top-level shims for any that the e2e/consumers still call, emitting a
one-line "moved to `legacy …`" deprecation note, for one release.
6. **Prune:** once nothing calls the shims, delete them; promote any `legacy`
command we've made leasing-native; delete `legacy` when empty.

Each phase is independently shippable and testable. Phases 1–3 are the high
ergonomic payoff; 4–6 are cleanup.

## Open questions for review

- `catalog model add` (noun-verb, 3-level) vs `catalog add-model` (verb-noun,
2-level)? Proposal: **noun-verb** — scales better, reads like `aivm`/`kubectl`.
- Keep top-level `secrets`/`logs`/`ps` aliases, or force the grouped form?
Proposal: **keep the few hottest as aliases.**
- `config set` backing store: a new `settings.yaml` in the config dir vs folding
into the existing `config.yaml`. Proposal: a small `settings.yaml` owned by the
leasing world (config.yaml stays the legacy profile artifact).
Loading
Loading