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
2 changes: 1 addition & 1 deletion docs/tests.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ $env:LLAMA_GUI_LLAMA_BIN_DIR = 'C:\path\to\llama\bin'
node tests/frontend/llama_flags_supported_unit.cjs --require-binaries
```

An explicit `LLAMA_GUI_LLAMA_BIN_DIR` (or legacy `LLAMA_CPP_BIN_DIR`) requires both executables and disables fallback to other installations. Missing binaries, failed/timed-out `--help`, and unsupported flags fail the check. `--require-binaries` also requires an explicit directory. This checks advertised flag names, including negated booleans; it does not validate enum values or GPU execution. Fork-only flags are excluded.
An explicit `LLAMA_GUI_LLAMA_BIN_DIR` (or legacy `LLAMA_CPP_BIN_DIR`) requires both executables and disables fallback to other installations. Missing binaries, failed/timed-out `--help`, and unsupported flags fail the check. `--require-binaries` also requires an explicit directory. This checks advertised flag names, including negated booleans; it does not validate enum values or GPU execution. Fork-only flags are excluded. Temporary `introduced_after` metadata permits a newly merged flag to be absent only from known builds at or below the last verified unsupported tag; newer and unknown builds remain strict. These windows are tracked in `upstream-changes.md`.

The Ubuntu and Windows/Python 3.13 CI jobs run `npm test`, including the unit and browser suites. The Ubuntu job also installs the `libgomp1` OpenMP runtime, downloads the exact CPU release in `tests/llama-cpp-pin.json`, verifies its SHA256, and sets the explicit directory for required binary compatibility. The Windows job uses the same optional binary check as the release workflow. Update the tag, asset, and checksum together from an official llama.cpp release, then run compatibility before accepting the new pin. No model or GPU is needed.

Expand Down
7 changes: 7 additions & 0 deletions docs/upstream-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ Track announced llama.cpp changes that may require coordinated Llama-GUI updates

## Pending

### Draft physical batch size — release compatibility window

- **Upstream:** [ggml-org/llama.cpp#30459](https://github.com/ggml-org/llama.cpp/pull/30459), merged October 14, 2026 as `3fb4767`.
- **Local handling:** Configure → Speculative Decoding exposes **Draft Physical Batch Size** (`--spec-draft-n-ubatch`) for server and CLI. Blank omits the argument; 0 inherits the target physical batch size. Smaller values can reduce draft compute-buffer VRAM. The setting is retained but omitted when speculation is disabled or only ngram methods are active.
- **Compatibility:** Verified the official b11617 Windows CPU server and CLI do not advertise this flag. Use a build containing the merge; leave the field blank on older binaries. The definition's temporary `introduced_after: "b11617"` allows known older builds (including the CI pin) to omit it in the compatibility check; newer and unknown builds remain strict. This records a last known unsupported build, not a promised first supported release.
- **Remaining:** Verify the first release advertising the flag, update the CI binary pin with its checksum, and remove `introduced_after` and its compatibility exemption once the supported floor includes it.

### Direct reads for lazy embedding tables (`--lazy-mode on-direct`)

- **Upstream:** [ggml-org/llama.cpp#28136](https://github.com/ggml-org/llama.cpp/pull/28136).
Expand Down
7 changes: 7 additions & 0 deletions tests/frontend/chat_popout_integration.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,13 @@ test("Recover chat here explains ownership contention and retries after the owne
assert.equal(await owner.locator("#chat-input").inputValue(), "Keep this unsent draft during recovery.");

await owner.close();
// Closing the page can finish before Chromium releases its Web Lock.
// Recovery intentionally uses ifAvailable, so retry only after release.
await observer.waitForFunction(async () => {
const { held } = await navigator.locks.query();
const name = window.LlamaGui.chatWindow._hostView.coordinator.lockName;
return !held.some(lock => lock.name === name);
});
await observer.locator(CHAT_RETURN_HERE).click();
await observer.locator("#chat-layout").waitFor({ state: "visible" });
assert.equal(await observer.locator("#chat-input").inputValue(), "Keep this unsent draft during recovery.");
Expand Down
21 changes: 21 additions & 0 deletions tests/frontend/flag_sync_smoke.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,26 @@ async function verifyReasoningPreserve(page) {
await page.fill("#config-search", "");
}

async function verifyDraftBatchSize(page) {
await selectSection(page, "configure");
await page.fill("#config-search", "draft physical batch");
const input = page.locator("#flag-draft_ubatch_size");
await input.waitFor({ state: "visible" });
assert.equal(await input.inputValue(), "");
assert.equal(await input.getAttribute("min"), "0");
await page.evaluate(() => window.LlamaGui.flagCore.setFlagValue("spec_type", "draft-mtp"));
await input.fill("8");
assert.equal(await page.evaluate(() => window.LlamaGui.flagCore.getFlagValues().draft_ubatch_size), 8);
assert.match(await page.textContent("#command-preview-text"), /--spec-draft-n-ubatch 8(?: |$)/);
await page.fill("#config-search", "context");
await page.fill("#config-search", "draft physical batch");
assert.equal(await input.inputValue(), "8", "Configure rebuild preserves the shared value");
await input.fill("0");
assert.match(await page.textContent("#command-preview-text"), /--spec-draft-n-ubatch 0(?: |$)/);
await input.fill("");
assert.doesNotMatch(await page.textContent("#command-preview-text"), /--spec-draft-n-ubatch/);
}

async function verifyNgramSimple(page) {
await selectSection(page, "configure");
await page.fill("#config-search", "ngram");
Expand Down Expand Up @@ -4580,6 +4600,7 @@ for (const phase of ["startup", "reload"]) {
}

for (const [name, verify = name] of [
["draft physical batch size", verifyDraftBatchSize],
["preset imports and safe notifications"],
["mirrored launch controls and Configure search"],
["API key controls and masked command previews"],
Expand Down
18 changes: 17 additions & 1 deletion tests/frontend/launch_args_unit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,21 @@ function launchResult() {
}
}

for (const tool of ["llama-server", "llama-cli"]) {
for (const specType of ["auto", "none", "ngram-simple", "draft-mtp", "draft-simple"]) {
for (const size of [undefined, 0, 8]) {
vm.runInContext(`window.LlamaGui.flagCore.setCurrentToolValue(${JSON.stringify(tool)});
window.LlamaGui.flagCore.applyFlagValues({ spec_type: ${JSON.stringify(specType)},
draft_ubatch_size: ${JSON.stringify(size)} });`, context);
const args = Array.from(flatLaunchArgs());
const index = args.indexOf("--spec-draft-n-ubatch");
const expected = size !== undefined && ["draft-mtp", "draft-simple"].includes(specType);
assert.equal(index >= 0, expected, `${tool}: draft batch ${size} with ${specType}`);
if (expected) assert.equal(args[index + 1], String(size));
}
}
}

for (const tool of ["llama-server", "llama-cli"]) {
vm.runInContext(`
window.LlamaGui.flagCore.setCurrentToolValue(${JSON.stringify(tool)});
Expand All @@ -802,7 +817,7 @@ for (const tool of ["llama-server", "llama-cli"]) {
vm.runInContext(`window.LlamaGui.flagCore.applyFlagValues({
spec_type: "none", model_draft: "draft.gguf", hf_repo_draft: "org/draft",
draft_sampling: "probabilistic",
draft_max: 8, draft_min: 2, draft_p_min: 0.5, draft_p_split: 0.1,
draft_max: 8, draft_min: 2, draft_ubatch_size: 8, draft_p_min: 0.5, draft_p_split: 0.1,
gpu_layers_draft: "all", draft_device: "CUDA0",
draft_cache_type_k: "q8_0", draft_cache_type_v: "q8_0",
ngram_simple: true, ngram_mod: true, ngram_map_k4v: true,
Expand All @@ -824,6 +839,7 @@ for (const tool of ["llama-server", "llama-cli"]) {
assert.equal(args[args.indexOf("--spec-type") + 1], "ngram-mod,ngram-map-k4v,ngram-simple");
assert.equal(args[args.indexOf("-md") + 1], "draft.gguf", "switching back restores saved draft arguments");
assert.equal(args[args.indexOf("--spec-draft-sampling") + 1], "probabilistic", "switching back restores draft sampling");
assert.equal(args[args.indexOf("--spec-draft-n-ubatch") + 1], "8", "switching back restores the draft batch size");
vm.runInContext(`window.LlamaGui.flagCore.setMultipleFlagValues({
ngram_simple: false, ngram_mod: false, ngram_map_k4v: false,
})`, context);
Expand Down
14 changes: 14 additions & 0 deletions tests/frontend/llama_flags_runner_unit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,18 @@ assert.match(oldBuild.output, /--no-legacy/);
const unknownBuild = run({ ...removedPinned, version: { status: 0, stdout: "custom fork build\n" } });
assert.equal(unknownBuild.exitCode, 1, "an unparseable binary version must keep the strict check");

const pendingFlag = [{ id: "draft_ubatch_size", flag: "--spec-draft-n-ubatch", tool: "both", introduced_after: "b11617" }];
for (const build of [11515, 11617, 11618, null]) {
const result = run({
...pinned, flagDefs: pendingFlag,
version: { status: 0, stdout: build === null ? "custom fork" : `build ${build}` },
});
assert.equal(result.exitCode, build !== null && build <= 11617 ? 0 : 1,
"only known older builds may omit a newly merged flag");
}
assert.equal(run({ ...pinned, flagDefs: pendingFlag,
help: { status: 0, stdout: "--spec-draft-n-ubatch N" },
version: { status: 0, stdout: "build 11618" },
}).exitCode, 0);

console.log("llama flag runner selection, strict failures and compatibility checks passed");
7 changes: 7 additions & 0 deletions tests/frontend/llama_flags_supported_unit.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ function collectUnsupportedFlags(flags, advertisedByTool, buildByTool) {
// from upstream builds on purpose; never hold them against a binary.
if (flag.fork_only) continue;
const removedInBuild = parseRemovedInBuild(flag.removed_in);
const introducedAfterBuild = parseRemovedInBuild(flag.introduced_after);
for (const option of [flag.flag, flag.false_flag].filter(Boolean)) {
for (const tool of expectedTools(flag)) {
const advertised = advertisedByTool[tool];
Expand All @@ -143,6 +144,12 @@ function collectUnsupportedFlags(flags, advertisedByTool, buildByTool) {
// docs/upstream-changes.md) stay in the GUI for older
// builds; builds at or above the removal tag may lack them.
const build = buildByTool[tool];
// Merged flags awaiting a release may be absent from known
// older builds. Unknown/newer builds must advertise them.
if (introducedAfterBuild !== null && build !== null && build <= introducedAfterBuild) {
console.log(`llama flag compatibility: ${tool}: ${option} requires a build after b${introducedAfterBuild} (see docs/upstream-changes.md)`);
continue;
}
if (removedInBuild !== null && build !== null && build >= removedInBuild) {
removedExemptions.push({ tool, id: flag.id, option, build, removedInBuild });
continue;
Expand Down
12 changes: 12 additions & 0 deletions ui/js/flags/definitions-speculative.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,18 @@ const FLAG_DEFINITIONS_SPECULATIVE = [
min: 0,
placeholder: "llama.cpp default",
},
{
id: "draft_ubatch_size",
flag: "--spec-draft-n-ubatch",
introduced_after: "b11617",
category: "speculative",
type: "int",
label: "Draft Physical Batch Size",
desc: "Physical maximum batch size of the draft context. Smaller values can reduce draft compute-buffer VRAM use. 0 inherits the main model's Physical Batch Size (-ub). Requires a build containing PR #30459 (not available in b11617 or earlier); leave blank for older builds.",
tool: "both",
min: 0,
placeholder: "0 (inherit main batch size)",
},
{
id: "draft_p_min",
flag: "--spec-draft-p-min",
Expand Down
1 change: 1 addition & 0 deletions ui/js/flags/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function shouldOmitSpeculativeFlag(f, values) {
const draftModelOnlyFlags = new Set([
"draft_max",
"draft_min",
"draft_ubatch_size",
"draft_p_min",
"draft_p_split",
"gpu_layers_draft",
Expand Down
Loading