Skip to content

fix(ai): resolve llama.cpp backend search path so on-device AI actually loads a model (I75) - #91

Merged
scotej merged 2 commits into
mainfrom
fix/i75-llama-backend-search-path
Jul 26, 2026
Merged

fix(ai): resolve llama.cpp backend search path so on-device AI actually loads a model (I75)#91
scotej merged 2 commits into
mainfrom
fix/i75-llama-backend-search-path

Conversation

@scotej

@scotej scotej commented Jul 26, 2026

Copy link
Copy Markdown
Owner

Summary

A same-day follow-up to 1.8.0/I73. That fixed the sidecar failing to spawn
at all (wrong exe path). This fixes the very next link in the same chain: the
sidecar now spawns fine and then crash-loops, because the model never
actually loads.

  • The pinned llama.cpp b9095 prebuilds are GGML_BACKEND_DL builds — the
    CPU (and, on Apple Silicon, Metal) backend ships as a separate library
    (ggml-cpu-*.dll / libggml-cpu.dylib / libggml-metal.dylib) that ggml
    dlopen()s at startup rather than links directly.
  • ggml_backend_load_best (ggml/src/ggml-backend-reg.cpp:479-489 at tag
    b9095, verified against the actual pinned source) globs exactly two
    places for that: the executable's own directory, and the process's current
    working directory. It never consults PATH / DYLD_FALLBACK_LIBRARY_PATH
    / LD_LIBRARY_PATH.
  • StudyVis's I73 fix already prepends those env vars, which is enough for the
    binary's linked imports (llama.dll, ggml-base.dll, …) — which is why
    the process starts and logs its banner — but it never satisfies the dlopen
    glob above. ggml_backend_reg_count() stays 0, model load fails with "no
    backends are loaded", and the crash-restart watcher gives up after 4
    identical failures. Confirmed against a friend's real Windows
    llama-server.log and against the actual llama-b9095-bin-win-cpu-x64.zip
    / llama-b9095-bin-macos-arm64.tar.gz release contents.

Fix

spawn_llama now also sets the child's working directory to the same
runtime-library directory already used for the env-var prepend
(Command::current_dir, present on tauri-plugin-shell 2.3.5), since
fs::current_path() is in ggml's search path. One code path covers both
engine sources (bundled, and the managed install where the runtime dir
already equals the binary's own directory) and all three platforms.

Checked for side effects: StudyVis's spawn_llama args
(--host/--port/--ctx-size/--n-gpu-layers/--model/--mmproj) never
pass --path or --slot-save-path — the two llama-server options with a
CWD-relative default — so changing the CWD doesn't affect anything else the
server does. model_path/mmproj_path are always absolute (file-picker- or
data-dir-derived, gated by is_file() before spawn).

Test plan

  • Traced the fix against the pinned llama.cpp b9095 source
    (ggml-backend-reg.cpp) and the real release archives for both
    Windows and macOS.
  • Confirmed no relative-path llama-server option StudyVis passes depends
    on CWD.
  • Not runnable on this box: no cargo/node toolchain and
    src-tauri/binaries/ has no fetched engine on this Linux dev host.
    Covered by CI (cargo test/clippy/fmt) on the PR, plus a live
    Windows/macOS walk before the release is published.

Fixes I75 (ISSUES.md). CHANGELOG.md carries the 1.8.1 section this PR needs
in place before Release prep can gate on it.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Fixed AI engine startup failures caused by companion libraries not being found during model loading.
    • Improved llama-server launch reliability across Windows and macOS.
    • Models now load successfully when the AI engine starts with its bundled runtime files.

…ly loads a model (I75)

1.8.0/I73 fixed the sidecar failing to spawn at all. It still crash-looped
after spawning: the pinned llama.cpp b9095 prebuilds dlopen their CPU/Metal
backend as a separate library, and ggml's loader for that only checks the
executable's own directory and the process's current working directory —
never PATH/DYLD_FALLBACK_LIBRARY_PATH/LD_LIBRARY_PATH, which only cover the
binary's linked imports. Spawn the sidecar with its companion-library
directory as its working directory so the backend is actually found.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 26, 2026 10:00

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 26, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@scotej, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 56 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 9f2d4d4b-a318-4774-a9a7-da84a4d43c44

📥 Commits

Reviewing files that changed from the base of the PR and between 5cec52f and adb5a45.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • ISSUES.md
📝 Walkthrough

Walkthrough

The llama-server child process now uses its runtime directory as its working directory, allowing GGML backend libraries to be located during model loading. The changelog and audit ledger document the fix and its cross-platform verification.

Changes

llama-server backend loading

Layer / File(s) Summary
Runtime directory and verification records
src-tauri/src/commands/sidecar.rs, ISSUES.md, CHANGELOG.md
spawn_llama sets the child process working directory to runtime_dir; the changelog and audit ledger record the backend discovery fix and verification.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • scotej/studyvis#88: Related changes to llama-server sidecar resolution and spawning behavior.

Suggested reviewers: copilot, claude

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the primary change: fixing llama.cpp backend discovery so the AI sidecar can load models.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/i75-llama-backend-search-path

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/src/commands/sidecar.rs`:
- Around line 556-566: Run the required validation gates for the Rust change:
repository-wide Prettier, src-tauri cargo fmt --check, cargo test, and cargo
clippy. Fix any reported formatting or lint issues, including existing Prettier
failures, and rerun all gates until they pass.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 75319681-5a7d-44fd-bb04-293b3e72b75f

📥 Commits

Reviewing files that changed from the base of the PR and between 4ff2cc1 and 5cec52f.

📒 Files selected for processing (3)
  • CHANGELOG.md
  • ISSUES.md
  • src-tauri/src/commands/sidecar.rs
📜 Review details
⏰ Context from checks skipped due to timeout. (2)
  • GitHub Check: Rust (macOS)
  • GitHub Check: Rust (Windows)
🧰 Additional context used
📓 Path-based instructions (3)
src-tauri/**/*.rs

📄 CodeRabbit inference engine (CLAUDE.md)

src-tauri/**/*.rs: Rust changes must preserve compatibility with persisted local data and peer-facing contracts.
Rust changes require cargo test, cargo fmt --check, and cargo clippy before review.

Files:

  • src-tauri/src/commands/sidecar.rs
**/*.{ts,tsx,js,jsx,css,json,md,rs,toml,yml,yaml}

📄 CodeRabbit inference engine (CLAUDE.md)

Run repository formatting before committing multi-file or subagent work; Prettier formatting must pass for the whole tree.

Files:

  • src-tauri/src/commands/sidecar.rs
  • CHANGELOG.md
  • ISSUES.md
CHANGELOG.md

📄 CodeRabbit inference engine (CLAUDE.md)

Update CHANGELOG.md as part of every release.

Files:

  • CHANGELOG.md
🔇 Additional comments (3)
src-tauri/src/commands/sidecar.rs (1)

556-566: LGTM!

ISSUES.md (1)

89-89: LGTM!

CHANGELOG.md (1)

21-38: LGTM!

Comment on lines +556 to +566
// I75: the prebuilt llama.cpp binary is a GGML_BACKEND_DL build —
// ggml-cpu-*/metal/blas are separate libs it dlopen()s at startup,
// not linked imports. ggml's loader (ggml_backend_load_best) globs
// exactly two places for those: the executable's own directory and
// the process's current working directory — never PATH /
// DYLD_FALLBACK_LIBRARY_PATH / LD_LIBRARY_PATH, which only satisfy
// the *linked* imports (llama.dll/ggml-base.dll etc, resolved below).
// Without this, the child starts and logs its banner, then dies with
// "no backends are loaded" the moment it tries to load a model.
command = command.current_dir(dir);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

npx prettier --check .

(
  cd src-tauri
  cargo fmt --check
  cargo test
  cargo clippy
)

Repository: scotej/studyvis

Length of output: 439


🏁 Script executed:

#!/usr/bin/env bash
set -euo pipefail

echo "Changed Prettier-warned files vs repository tracked:"
npx prettier --check CHANGELOG.md ISSUES.md

echo
echo "Rust toolchain / src-tauri status:"
(
  if command -v cargo >/dev/null 2>&1; then
    cargo --version || true
    source .cargo/config.toml 2>/dev/null || true
    (
      cd src-tauri
      cargo fmt --check || true
      cargo test --no-run --message-format=short || true
      cargo clippy -- -D warnings --no-deps || true
    ) || true
  else
    echo "cargo not available"
  fi
)

echo
echo "Relevant git diff stat/name-only:"
git diff --stat || true
git diff --name-only || true

Repository: scotej/studyvis

Length of output: 492


Run the required validation gates before merging.

This PR changes Rust code, so the required gates are: repository-wide Prettier, src-tauri/cargo fmt --check, src-tauri/cargo test, and src-tauri/cargo clippy. Prettier currently reports formatting issues in repository files that need to be addressed.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/commands/sidecar.rs` around lines 556 - 566, Run the required
validation gates for the Rust change: repository-wide Prettier, src-tauri cargo
fmt --check, cargo test, and cargo clippy. Fix any reported formatting or lint
issues, including existing Prettier failures, and rerun all gates until they
pass.

Source: Coding guidelines

@scotej
scotej merged commit cf0376a into main Jul 26, 2026
4 checks passed
@scotej
scotej deleted the fix/i75-llama-backend-search-path branch July 26, 2026 10:06
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.

2 participants