Skip to content

feat(lucebox): autotune sweep + profile/smoke diagnostics (stacked on #335)#5

Open
easel wants to merge 10 commits into
feat/lucebox-clifrom
feat/lucebox-tuning
Open

feat(lucebox): autotune sweep + profile/smoke diagnostics (stacked on #335)#5
easel wants to merge 10 commits into
feat/lucebox-clifrom
feat/lucebox-tuning

Conversation

@easel

@easel easel commented Jun 16, 2026

Copy link
Copy Markdown
Owner

Stacked on Luce-Org#335 (core lucebox CLI). Base is `feat/lucebox-cli`, so this PR's diff is only the tuning/diagnostics cluster. Review/merge after Luce-Org#335.

Summary

Restores the second-order tuning + diagnostics surface deferred from Luce-Org#335:

  • autotune.py: the empirical sweep machinery — `candidate_configs`, the `Profile` registry + per-arch coding-agent-loop brackets, `get_profile`. (`recommend_preset` stays in `download.py` from the feat(lucebox): hub CLI + autotune/sweep/profile + harness adapters + shell wrapper Luce-Org/lucebox#335 split.)
  • sweep.py: the per-cell sweep driver (config set → restart → luce-bench snapshot → winner pick).
  • profile.py / smoke.py + the `autotune`, `profile`, `smoke` CLI commands.
  • Wrapper: the `autotune --sweep` exec-routing special case + autotune/profile/smoke in usage/completion/exec-set.

Tests

sweep / profile / smoke / autotune-cli / candidate-configs suites + the Profile/bracket tests restored. `test_cli` guards that the client launcher verbs stay deferred while autotune/profile/smoke register.

lucebox 122 passed (123 with the inherited recommend_preset test), wrapper 55 passed, ruff + mypy + shellcheck clean.

🤖 Generated with Claude Code

easel and others added 7 commits June 16, 2026 11:18
… adapters

New lucebox/ Python package exposing the hub CLI (autotune, sweep,
profile, smoke, models, config, download, host-check, docker_run) plus
the lucebox.sh launcher wrapper and install.sh. Adds the harness/
adapter package wrapping external coding agents (claude_code, codex,
hermes, openclaw, opencode, pi) that autotune sweeps drive. Ships
scripts/check_lucebox_wrapper_sandbox.sh and scripts/test_lucebox_sh.sh
for wrapper validation, full pytest coverage under lucebox/tests/, and
the bragi autotune profile-sweep protocol docs.

This is the user-facing surface of lucebox-hub: one CLI to launch the
image, tune layer-split / pflash settings against a host, run sweeps,
and dispatch bench runs. Splitting it out keeps Python-side review
independent of the C++ server and Docker stack reviews.

- Luce-Org#334 (docker-stack): docker_run.py launches the lucebox-hub image
- Luce-Org#337 (lucebench-harness): lucebox bench delegates to luce-bench (workspace dep)
- Luce-Org#336 (server-layer-split): autotune presumes layer-split build artifacts
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…to-auto-pick

entrypoint.sh:write_host_info() bailed loudly when /opt/lucebox-hub/ did not
exist on the host (unit tests, plain docker run without bind mount), because
bash refuses the > redirect before the command runs and 2>/dev/null does not
suppress the redirect's own error. Guard with an upfront [ -d ] check.

test_lucebox_sh.sh:test_entrypoint_multi_target was asserting against the
pre-Luce-Org#334 multi-target semantics (auto-pick + warn + exec shim). PR Luce-Org#334
(merged) changed that to refuse-to-auto-pick + exit non-zero. Update the
assertion: still drives the auto-detect block (so any DRAFT_FAMILY_GLOB
set -u regression trips), but now requires the refuse warn to fire and the
shim to NOT exec.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds the missing rc != 0 assertion to test_entrypoint_multi_target. The
previous fix updated the test to look for the "Refusing to auto-select"
warn and a non-exec'd shim, but didn't check the exit code. A regression
where the entrypoint logged the warn but still exited 0 (silently
auto-picking under the covers) would have slipped through. The container
MUST fail to start on multi-target ambiguity — that is the whole point
of the policy added in Luce-Org#334.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e CI

Behavior-preserving cleanup of the lucebox hub CLI surfaced by review:

- cli.py: delete dead _pick_variant_from_driver; collapse the six
  near-identical Typer client subcommands into a factory loop.
- harness adapters: rewire claude_code onto _common; add build_base_parser
  + run_main so the six main() arg-parsers stop duplicating (per-client
  differences preserved); hoist KNOWN_AREAS in bench.py.
- Unify DFLASH_ALLOWLIST in types.py (was duplicated with a silent
  one-field divergence between cli.py and sweep.py).
- config.py: extract _atomic_write_doc.
- Shell: shared _append_host_env / _append_scalar_env / _set_tty_flags
  helpers in lucebox.sh, _trim in entrypoint.sh, sandbox factory in the
  test runner.
- FIX a tty regression introduced while sharing the docker interactive
  flags: the helper ran behind `< <(...)`, so `[ -t 1 ]` tested the pipe
  and forced -i even on a real tty (breaking interactive client TUIs).
  Now runs in caller scope via nameref; added a PTY-based regression test.
- Add unit tests for the previously-untested harness adapters + bench, and
  wire lucebox + harness pytest into CI (it previously never ran pytest).

116 -> 156 tests; bash wrapper 54 -> 55; ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oad core

Defer second-order features to follow-up PRs so this PR is just the host
wrapper and the CLI needed to install, launch/serve, configure, and
download models. Cuts the PR from ~11.2k to ~6.2k added lines.

Deferred out of this PR (land as stacked follow-ups):
- Agent-client adapters + bench: the entire net-new harness/ package
  (bench.py, the six clients, run_lucebench.sh) and the client launcher
  verbs. harness/ reverts to main's loose-scripts state; the root
  pyproject no longer adds harness as a workspace member/dep.
- Autotune sweep + profiles: candidate_configs, the Profile registry and
  per-arch brackets, sweep.py, and the `autotune` command. The host-derived
  DFLASH_* heuristic (runtime_from_host) STAYS — config.live_config needs it
  to bake serve defaults — slimmed into autotune.py.
- profile + smoke commands and their modules.

Coupling fixes:
- recommend_preset moved autotune.py -> download.py (models sub-app uses it).
- Wrapper: drop the autotune --sweep exec-routing special case and trim
  usage/completion/exec-set to the core verbs.

Tests/CI follow the surface: deferred-feature tests removed; runtime_from_host
heuristic tests kept; new guard asserts the deferred verbs are NOT registered.
CI pytest step scoped to lucebox. lucebox 72 passed, wrapper 53 passed,
ruff + mypy + shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…utotune)

The exhaustive VRAM-tier test for recommend_preset lived in the deferred
test_autotune_cli.py; since the function moved to download.py in the split,
its direct test belongs alongside it. The models auto-recommend path is
also covered indirectly by test_models_cli.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 39087d8adb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lucebox.sh
local cmd="$1"; shift
case "$cmd" in
config|models|check|print-run|print-serve-argv)
config|smoke|models|check|profile|print-run|print-serve-argv)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep profile off the exec fast path

When the server container is already running, this routes lucebox profile through cmd_exec_in_container. But profile.run_profile immediately shells out to docker inspect/docker exec to run luce-bench in the server container, while the long-running server container is started without the Docker socket mounted. In the normal “server running” case the nested profile command therefore cannot see Docker and exits before taking a snapshot; route profile through the docker-run orchestrator like autotune --sweep.

Useful? React with 👍 / 👎.

if rc is not None:
return rc

cfg = config_mod.load() or config_mod.live_config()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve scalar env overrides during sweeps

When config.toml exists, this raw load ignores the LUCEBOX_PORT, LUCEBOX_CONTAINER, and LUCEBOX_MODELS values that the wrapper exports and the systemd unit uses. With a non-default port/container, the sweep restarts the right service but then waits on cfg.port or profiles cfg.container_name from the stale/default file values, causing every cell to fail readiness or target the wrong container. Use the same env overlay path as the CLI before building candidates and probing the server.

Useful? React with 👍 / 👎.

…eftovers

The serve-argv builder (docker_run.py) — the core's whole job — was only
50% covered by one test. Add lucebox/tests/test_docker_run.py pinning the
argv/printable contract, every DFLASH_* env (always-on + conditional),
target/draft path resolution, host-env forwarding, volume mounts, and
docker_pull. docker_run.py 50% -> 96%.

Includes a regression guard for the preset-cap analysis: a large preset
serves at the conservative DflashRuntime() floor (max_ctx=16384), not a
high OOM-prone ctx — the VRAM-tier heuristic's higher caps only apply via
`autotune --apply` (which threads cfg.model.preset; separate PR). No bug.

Also:
- Add lucebox/**/*.py to the ruff include set (was mypy-checked but never
  ruff-linted) and clear the 3 unused imports it surfaced in cli.py.
- Drop an orphan `# autotune` section divider and refresh two docstrings
  that referenced the deferred autotune subcommand.

lucebox 89 passed, wrapper 53 passed, ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@easel easel force-pushed the feat/lucebox-tuning branch from 39087d8 to 9f63eea Compare June 16, 2026 23:12
easel and others added 2 commits June 17, 2026 01:06
Core carried three bits only the deferred follow-ups use. Evict them so
the core CLI is just the basic launch/config/download surface:

- types.BASE_DFLASH_ALLOWLIST — no core caller; used by the sweep's
  per-cell bracket. Moves to the tuning PR.
- autotune.runtime_from_host preset-awareness (_preset_approx_gb + the
  ≥20 GB large-model 32K branch) — core always calls runtime_from_host(host)
  with no preset, so this was unreachable here. The preset-size cap moves
  to the tuning PR alongside autotune --apply (which threads cfg.model.preset).
  Behavior for the core's preset-less call is unchanged.
- config.live_config(preset_name=...) — dead across the entire tree (every
  call site passes no arg). Deleted outright, not moved.

Their tests move with them. lucebox 87 passed, wrapper 53, ruff + mypy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Stacked on the core lucebox CLI (Luce-Org#335). Restores the second-order tuning
and diagnostics surface deferred from that PR:

- autotune.py: the empirical sweep machinery — candidate_configs, the
  Profile registry + per-arch coding-agent-loop brackets, get_profile.
  (recommend_preset stays in download.py from the core split.)
- sweep.py: the per-cell config sweep driver (config set -> restart ->
  luce-bench snapshot -> winner pick).
- profile.py / smoke.py + the `autotune`, `profile`, `smoke` CLI commands.
- Wrapper: the `autotune --sweep` exec-routing special case (sweeps must
  stay on docker run, not exec into the container they'd restart) and the
  autotune/profile/smoke entries in usage + completion + the exec set.

Tests: sweep / profile / smoke / autotune-cli / candidate-configs suites
and the Profile/bracket tests restored; test_cli guards that the client
launcher verbs are still deferred while autotune/profile/smoke register.

lucebox 122 passed, wrapper 55 passed, ruff + mypy + shellcheck clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@easel easel force-pushed the feat/lucebox-tuning branch from 9f63eea to a9a1f30 Compare June 17, 2026 05:09
@easel easel force-pushed the feat/lucebox-cli branch 2 times, most recently from 0ae47c5 to 573570a Compare July 6, 2026 19:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant