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
14 changes: 8 additions & 6 deletions .claude/skills/add-anima-model/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,8 @@ local test fixture is the exception and is deliberately not this -- see "Test da
## 4. Verify before uploading, not after

```bash
cargo run --release --example load -- models/<stem>.yaml cuda
cargo run --release --example generate -- models/<stem>.yaml "<prompt>" cuda
cargo run --release -p waifu-tools --bin load -- models/<stem>.yaml cuda
cargo run --release -p waifu-tools --bin generate -- models/<stem>.yaml "<prompt>" cuda
```

`load` builds the whole model from the manifest and encodes a prompt with it -- weights,
Expand Down Expand Up @@ -327,7 +327,7 @@ model and not on the pictures. Add `not-for-all-audiences` if the samples warran

## 6. Wire it into the CLI

`waifu/src/cli/hub.rs`, once the model is actually on both hubs and not before -- an entry naming
`waifu-modelmanager/src/hub.rs`, once the model is actually on both hubs and not before -- an entry naming
a manifest that is not published is a name that 404s for everyone who types it.

- a `Published` entry in `CATALOG`: `name`, `full_name` (what the picker shows), `repo`,
Expand Down Expand Up @@ -358,11 +358,13 @@ entry makes that paragraph wrong as well as incomplete.

```bash
./build/unittest
cargo test --release --manifest-path waifu/Cargo.toml --features cli -- --test-threads=1
LIBWAIFU_LIB_DIR="$PWD/build" cargo test --release --manifest-path waifu/Cargo.toml -- --test-threads=1
cargo test --release -p waifu-modelmanager
LIBWAIFU_LIB_DIR="$PWD/build" cargo test --release -p waifu-cli
```

`--features cli` is what compiles `src/cli/`, so without it the `hub.rs` tests you just changed do
not run at all.
The `hub.rs` tests you just changed are `waifu-modelmanager`'s, and the picker and the `-m` usage
text read the catalogue through its C API, which is what `waifu-cli`'s tests check.

The model tests are `#[ignore]`d and need a real package. **Do not run them unless the user asks**
(`CLAUDE.md`); say which were skipped and what runs them. When asked, and for a change that
Expand Down
22 changes: 13 additions & 9 deletions .claude/skills/add-sdxl-model/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -262,11 +262,11 @@ resolves them without listing them in `names()`.

## 4. Verify before uploading, not after

Two examples, in this order:
Two tools, in this order:

```bash
cargo run --release --example load -- models/<stem>.yaml cuda
cargo run --release --example generate -- models/<stem>.yaml "<prompt>" cuda
cargo run --release -p waifu-tools --bin load -- models/<stem>.yaml cuda
cargo run --release -p waifu-tools --bin generate -- models/<stem>.yaml "<prompt>" cuda
```

`load` assembles the whole model from the manifest and encodes a prompt with it, which is the
Expand Down Expand Up @@ -356,8 +356,9 @@ the fine tunes differ: base is `openrail++`, WAI is `cdla-permissive-2.0`, NoobA

## 6. Wire it into the CLI

Only two edits in `waifu/src/cli/hub.rs` -- the picker and the `-m` usage text both build
themselves from `hub::names()`, so nothing else needs telling:
Only two edits in `waifu-modelmanager/src/hub.rs` -- the picker and the `-m` usage text both build
themselves from the catalogue, which libwaifu_modelmanager hands out, so nothing else needs
telling:

- add a `Published` entry to `CATALOG` (`name`, `repo`, `manifest`)
- add the unversioned alias to `ALIASES`
Expand All @@ -382,10 +383,12 @@ Then `README.md`: the model table, the `waifu -m` example list, and a `Recent up

```bash
./build/unittest
cargo test --manifest-path waifu/Cargo.toml --features cli -- --test-threads=1
LIBWAIFU_LIB_DIR="$PWD/build" cargo test --manifest-path waifu/Cargo.toml -- --test-threads=1
cargo test -p waifu-modelmanager
LIBWAIFU_LIB_DIR="$PWD/build" cargo test -p waifu-cli
```

Both flags matter -- see traps. To run the SDXL numerical tests you also need `models/`
`--test-threads=1` matters -- see traps. The `hub.rs` tests are `waifu-modelmanager`'s. To run the SDXL numerical tests you also need `models/`
populated; see "Test data" below.

## Traps
Expand All @@ -395,7 +398,7 @@ populated; see "Test data" below.
| configure stops on `invalid CUTLASS_ROOT` | `third_party/cutlass` is not vendored, and a CUDA build requires it | `third_party/install_cutlass.sh` |
| a CUDA build takes tens of minutes longer than expected | `install_flash_attn.sh` was run out of habit: it compiles every kernel for `sm_80`, `sm_86`, `sm_89`, `sm_90` and `sm_120` | skip it. Since `dd804f1` a build without it breaks nothing -- `stores_and_reads_a_paged_kv_cache` asks `F::paged_attention_available()` and skips itself, and `cuda/flash_attn_test.cc` is only compiled when the option is on. Wanted anyway? Pin `FLASH_ATTN_CUDA_ARCH` to this card's arch |
| every `sdxl.rs` test fails with `Aborted: out of memory` | `cargo test` runs one thread per core, each loading a 7 GB model onto one GPU | `-- --test-threads=1` |
| `hub.rs` tests never run | `cli` is a Cargo feature; plain `cargo test` compiles none of `src/cli/` | `--features cli` |
| `hub.rs` tests never run | they are `waifu-modelmanager`'s, and `--manifest-path waifu/Cargo.toml` tests only `waifu` | `cargo test -p waifu-modelmanager` |
| CPU verify takes 20+ minutes | no CUDA in the build | build with CUDA |
| the export dies on a 106-byte "checkpoint" | a Civitai download without a valid token writes the JSON error body to the output file and `curl` still exits `0` | check it is not JSON (`file <name>.safetensors`) before exporting; verify the sha256 |
| a link error in `cargo test` that makes no sense | the disk filled: a debug `cargo test` builds a second dependency tree beside the release one cmake already built | `--release` on the Rust suite, and delete each checkpoint the moment its export finishes |
Expand Down Expand Up @@ -441,4 +444,5 @@ whole second dependency tree beside the release one CMake already built. On a bo
that fills the disk, and a full disk here does not announce itself: the tool cannot even create
the file it writes command output to, so *every* command fails before it runs, and the failure
before that one is a linker error that looks like a code problem. Run the Rust suite as
`cargo test --release --manifest-path waifu/Cargo.toml --features cli`.
`cargo test --release --manifest-path waifu/Cargo.toml`, and the catalogue's as
`cargo test --release -p waifu-modelmanager`.
39 changes: 39 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ jobs:
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Test (model manager)
run: cargo test -p waifu-modelmanager

- name: Test (command line)
run: cargo test -p waifu-cli
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Build (tools)
run: cargo build -p waifu-tools
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

macos-metal:
name: macOS (CPU + Metal)
runs-on: macos-latest
Expand All @@ -48,6 +61,19 @@ jobs:
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Test (model manager)
run: cargo test -p waifu-modelmanager

- name: Test (command line)
run: cargo test -p waifu-cli
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Build (tools)
run: cargo build -p waifu-tools
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

windows-cuda:
name: Windows (CUDA)
runs-on: windows-2022
Expand Down Expand Up @@ -116,3 +142,16 @@ jobs:
run: cargo test --manifest-path waifu/Cargo.toml
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Test (model manager)
run: cargo test -p waifu-modelmanager

- name: Test (command line)
run: cargo test -p waifu-cli
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build

- name: Build (tools)
run: cargo build -p waifu-tools
env:
LIBWAIFU_LIB_DIR: ${{ github.workspace }}/build
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ jobs:
# The Developer ID certificate goes into a keychain of the job's own, made the default so
# that codesign finds the identity and notarytool the profile, and deleted when the job ends.
# The notary profile is an App Store Connect API key, under the name a developer's machine
# gives the scripts too (macos/README.md).
# gives the scripts too (waifu-app/swiftui/README.md).
- name: Set up signing
env:
CERT_P12: ${{ secrets.MACOS_CERT_P12 }}
Expand Down Expand Up @@ -80,7 +80,7 @@ jobs:
- name: Build app
env:
WAIFU_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
run: macos/build-app.sh
run: waifu-app/swiftui/build-app.sh

# Signed, notarized and stapled. Apple's notary service takes minutes as a rule, and can take
# an hour or two over an account's first submissions.
Expand All @@ -89,11 +89,11 @@ jobs:
WAIFU_SIGN_IDENTITY: ${{ secrets.MACOS_SIGN_IDENTITY }}
WAIFU_NOTARY_PROFILE: waifu-notary
run: |
macos/package-dmg.sh
waifu-app/swiftui/package-dmg.sh
# Named for what it is, which version, and the one architecture it runs on: the runner
# is Apple silicon, and the app is built for nothing else.
dmg="libwaifu-gui-$WAIFU_VERSION-macos-arm64.dmg"
cp macos/build/libwaifu-gui.dmg "$dmg"
cp waifu-app/swiftui/build/libwaifu-gui.dmg "$dmg"
echo "DMG=$dmg" >> "$GITHUB_ENV"

- name: Upload artifact
Expand Down
15 changes: 11 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ cmake --build build -j$(sysctl -n hw.ncpu)

## Test

Before committing, both fast suites must pass:
Before committing, the fast suites must pass:

```bash
./build/unittest
LIBWAIFU_LIB_DIR="$PWD/build" cargo test --manifest-path waifu/Cargo.toml
cargo test -p waifu-modelmanager
LIBWAIFU_LIB_DIR="$PWD/build" cargo test -p waifu-cli
LIBWAIFU_LIB_DIR="$PWD/build" cargo build -p waifu-tools
```

`LIBWAIFU_LIB_DIR` must be absolute because the Rust build script runs from `waifu/`.
`LIBWAIFU_LIB_DIR` must be absolute because the build scripts run from their crates' directories.

## Licence

Expand Down Expand Up @@ -55,14 +58,14 @@ cargo test --release --manifest-path waifu/Cargo.toml --no-fail-fast \
cargo test --release --manifest-path waifu/Cargo.toml --no-fail-fast \
--test cosyvoice3 -- --ignored --test-threads=1

# waifu/src/indextts/, tools/indextts_* (campplus.rs and features.rs: run cosyvoice3's too)
# waifu/src/indextts/, waifu/src/text_normalizer/, tools/indextts_*
# (campplus.rs, features.rs and text_normalizer/: run cosyvoice3's too)
# needs models/indextts25.yaml and the two recordings tools/indextts_reference_voices.py writes
cargo test --release --manifest-path waifu/Cargo.toml --no-fail-fast \
--test indextts --test indextts_gpt -- --ignored --test-threads=1
```

- Keep `--no-fail-fast` and `--test-threads=1`; each test loads a full model onto the GPU.
- Do not add `--features cli`.
- Run only one GPU job at a time. Check `nvidia-smi` before diagnosing OOM or timing failures.
- No individual test may exceed three minutes. Reuse model fixtures across tests instead of
loading a model per test. The harness gives every test its own thread, so a thread-local
Expand All @@ -86,6 +89,10 @@ cargo test --release --manifest-path waifu/Cargo.toml --no-fail-fast \
removed from the runtime and the writer: a package that has them no longer loads and has to be
exported again. A ControlNet package must use the same format as the model it steers.

## Apps

Do not change the behavior of `waifu-app/swiftui` or `waifu-app/wpf` unless the user asks.

## Git

- Never modify, commit to, or push directly to `main`. Use a non-`main` branch and a pull request.
Expand Down
108 changes: 71 additions & 37 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -628,60 +628,94 @@ file(GENERATE

# CMake drives the Rust build too, so a plain `cmake --build build` is the whole build: no
# separate `cargo build` step. LIBWAIFU_LIB_DIR is set explicitly so this works regardless of where
# the build directory was configured, matching what waifu/build.rs falls back to on its own.
# the build directory was configured, matching what the build scripts fall back to on their own.
#
# CARGO_TARGET_DIR points inside the build directory as well, so that a build writes nothing
# outside it and two build directories configured differently do not fight over one cache. Cargo
# still puts the binary under a profile directory of its own, so the last step copies it up beside
# the C++ executables, where someone who built in `build` expects to find it.
# still puts what it builds under a profile directory of its own, so each target copies it up
# beside the C++ executables, where someone who built in `build` expects to find it.
find_program(CARGO_EXECUTABLE cargo REQUIRED)
set(waifu_CARGO_DIR "${CMAKE_BINARY_DIR}/cargo")
set(waifu_CLI "${waifu_CARGO_DIR}/release/waifu${CMAKE_EXECUTABLE_SUFFIX}")
# `--features cli` is what builds the `waifu` binary: it is off by default so that using the crate
# as a library does not drag in a terminal library.
set(waifu_CARGO_FEATURES "cli")
add_custom_target(waifu-cli ALL
COMMAND ${CMAKE_COMMAND} -E env "LIBWAIFU_LIB_DIR=${CMAKE_BINARY_DIR}"
"CARGO_TARGET_DIR=${waifu_CARGO_DIR}"
${CARGO_EXECUTABLE} build -p waifu --features ${waifu_CARGO_FEATURES} --release
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${waifu_CLI}" "${CMAKE_BINARY_DIR}/waifu${CMAKE_EXECUTABLE_SUFFIX}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS flint "${CMAKE_BINARY_DIR}/flint_link_flags.txt" ${flint_CARGO_DEPENDS}
COMMENT "cargo build -p waifu --features ${waifu_CARGO_FEATURES} --release"
VERBATIM)
set(waifu_CARGO_OUT "${waifu_CARGO_DIR}/release")

# The C API, as a shared library an app links: libwaifu.dylib, libwaifu.so or waifu.dll, beside the
# executables, and waifu.h under include/. See docs/ffi.md.
# The two libraries, and the command line built on their C APIs. See docs/ffi.md.
#
# libwaifu the engine: libwaifu.dylib, libwaifu.so or waifu.dll
# libwaifu_modelmanager the catalogue and fetching, with no engine and no flint behind it
#
# Cargo names the library after its crate, waifu_ffi, so the copy gives it the name its install
# name says it has. Its own build script puts the Metal kernels into it and writes the header.
# Each is copied beside the executables with its header under include/. The two share nothing,
# headers included. On Windows the import library goes too, which is what a program linking the
# DLL is linked against.
if (APPLE)
set(waifu_FFI_BUILT "${waifu_CARGO_DIR}/release/libwaifu_ffi.dylib")
set(waifu_FFI "${CMAKE_BINARY_DIR}/libwaifu.dylib")
set(waifu_LIB_PREFIX "lib")
set(waifu_LIB_SUFFIX ".dylib")
elseif (WIN32)
set(waifu_FFI_BUILT "${waifu_CARGO_DIR}/release/waifu_ffi.dll")
set(waifu_FFI "${CMAKE_BINARY_DIR}/waifu.dll")
set(waifu_LIB_PREFIX "")
set(waifu_LIB_SUFFIX ".dll")
else()
set(waifu_FFI_BUILT "${waifu_CARGO_DIR}/release/libwaifu_ffi.so")
set(waifu_FFI "${CMAKE_BINARY_DIR}/libwaifu.so")
set(waifu_LIB_PREFIX "lib")
set(waifu_LIB_SUFFIX ".so")
endif()
add_custom_target(waifu-ffi ALL

# waifu_copy_commands(<out> <library> <header dir> <header>): the commands that copy a library
# cargo built, and its header, up beside the executables.
function(waifu_copy_commands out library header_dir header)
set(commands
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${waifu_CARGO_OUT}/${waifu_LIB_PREFIX}${library}${waifu_LIB_SUFFIX}"
"${CMAKE_BINARY_DIR}/${waifu_LIB_PREFIX}${library}${waifu_LIB_SUFFIX}")
if (WIN32)
list(APPEND commands
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${waifu_CARGO_OUT}/${library}.dll.lib" "${CMAKE_BINARY_DIR}/${library}.dll.lib")
endif()
list(APPEND commands
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/include"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/${header_dir}/${header}" "${CMAKE_BINARY_DIR}/include/${header}")
set(${out} ${commands} PARENT_SCOPE)
endfunction()

# The engine. Its own build script reads flint_link_flags.txt, puts the Metal kernels into it and
# writes the header.
waifu_copy_commands(waifu_ENGINE_COPY waifu waifu/include waifu.h)
add_custom_target(waifu-engine ALL
COMMAND ${CMAKE_COMMAND} -E env "LIBWAIFU_LIB_DIR=${CMAKE_BINARY_DIR}"
"CARGO_TARGET_DIR=${waifu_CARGO_DIR}"
${CARGO_EXECUTABLE} build -p waifu-ffi --release
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${waifu_FFI_BUILT}" "${waifu_FFI}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_BINARY_DIR}/include"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${CMAKE_CURRENT_SOURCE_DIR}/waifu-ffi/include/waifu.h"
"${CMAKE_BINARY_DIR}/include/waifu.h"
${CARGO_EXECUTABLE} build -p waifu --lib --release
${waifu_ENGINE_COPY}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS flint "${CMAKE_BINARY_DIR}/flint_link_flags.txt" ${flint_CARGO_DEPENDS}
COMMENT "cargo build -p waifu-ffi --release"
COMMENT "cargo build -p waifu --lib --release"
VERBATIM)

# The model manager. It links nothing native, so it needs nothing CMake built.
waifu_copy_commands(waifu_MODELMANAGER_COPY waifu_modelmanager waifu-modelmanager/include waifu_modelmanager.h)
add_custom_target(waifu-modelmanager ALL
COMMAND ${CMAKE_COMMAND} -E env "CARGO_TARGET_DIR=${waifu_CARGO_DIR}"
${CARGO_EXECUTABLE} build -p waifu-modelmanager --lib --release
${waifu_MODELMANAGER_COPY}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "cargo build -p waifu-modelmanager --lib --release"
VERBATIM)

# The command line, `waifu`, which calls the two libraries above through nothing but their C APIs,
# with both linked into it statically: one file, which needs neither library beside it.
set(waifu_CLI "${waifu_CARGO_OUT}/waifu${CMAKE_EXECUTABLE_SUFFIX}")
add_custom_target(waifu-cli ALL
COMMAND ${CMAKE_COMMAND} -E env "LIBWAIFU_LIB_DIR=${CMAKE_BINARY_DIR}"
"CARGO_TARGET_DIR=${waifu_CARGO_DIR}"
${CARGO_EXECUTABLE} build -p waifu-cli --release
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${waifu_CLI}" "${CMAKE_BINARY_DIR}/waifu${CMAKE_EXECUTABLE_SUFFIX}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "cargo build -p waifu-cli --release"
VERBATIM)

# One cargo at a time in one target directory: two would wait on each other's lock anyway, and
# this way the second reuses what the first built.
add_dependencies(waifu-ffi waifu-cli)
# this way each reuses what the one before built.
add_dependencies(waifu-modelmanager waifu-engine)
add_dependencies(waifu-cli waifu-engine waifu-modelmanager)

set(unittest_LIBADD
flint_test
Expand Down
Loading
Loading