## Table Of Contents
1. [How-To User Guides](#1-how-to-user-guides)
1. [Recipe Map](#recipe-map)
2. [First-Time Setup](#2-first-time-setup)
1. [Install The Package](#install-the-package)
2. [Run The First Command](#run-the-first-command)
3. [Configure Local Environment Files](#configure-local-environment-files)
4. [Register A Persistent Storage Root](#register-a-persistent-storage-root)
3. [Dataset Workflows](#3-dataset-workflows)
1. [Sync A Manifest Workbook](#sync-a-manifest-workbook)
2. [Sync A Training Dataset](#sync-a-training-dataset)
3. [Configure Caption Outputs](#configure-caption-outputs)
4. [LoRA Workflows](#4-lora-workflows)
1. [Write SimpleTuner Artifacts](#write-simpletuner-artifacts)
2. [Launch SimpleTuner Training](#launch-simpletuner-training)
3. [Showcase Training Checkpoints](#showcase-training-checkpoints)
4. [Promote A Training Checkpoint](#promote-a-training-checkpoint)
5. [Image Workflows](#5-image-workflows)
1. [Generate Solo And Duo T2I Runs](#generate-solo-and-duo-t2i-runs)
2. [Convert PNG Files To JPEG](#convert-png-files-to-jpeg)
3. [Prepare Image Sets](#prepare-image-sets)
4. [Tag And Caption Images](#tag-and-caption-images)
6. [Maintainer Checks](#6-maintainer-checks)
1. [Sync Dependencies](#sync-dependencies)
2. [Run Tests](#run-tests)
7. [Troubleshooting](#7-troubleshooting)
1. [Environment Problems](#environment-problems)
2. [Command Problems](#command-problems)
3. [Manifest And Config Problems](#manifest-and-config-problems)
# 1. How-To User Guides
## Recipe Map
Use this file when you want commands in order. Use
[References](References.md) when you need exact names and
[Explanations](Explanations.md) when you need the system model.
> [!IMPORTANT]
> Runtimeful dataset, training, ComfyUI, and LLM examples assume that
> `KNF_STORAGE` is exported or root `--storage NAME_OR_PATH` is present. An
> explicit workflow config path does not replace storage selection.
> [!NOTE]
> Related: use [docs standards](README.md#2-documentation-standards) when adding
> new recipes so headings, callouts, and links stay consistent.
# 2. First-Time Setup
## Install The Package
Use this recipe from the Kneiff repository root.
1. Create or activate a Python environment:
```bash
python -m venv .venv
source .venv/bin/activate
.venv/bin/python -m pip install --upgrade pip
```
2. Install the package for runtime use:
```bash
.venv/bin/python -m pip install -e "."
```
3. Install the maintainer tools when you plan to edit the project:
```bash
.venv/bin/python -m pip install -e "." --group dev
```
4. If you use `uv`, sync the locked environment:
```bash
uv sync --locked --all-extras
```
> [!NOTE]
> Related: use [dependency surfaces](References.md#dependency-surfaces) for the
> difference between runtime dependencies and dependency groups.
## Run The First Command
Run the CLI after installation:
```bash
knf --help
```
Check the top-level command groups:
```bash
knf dataset --help
knf train --help
knf img --help
knf comfy --help
```
> [!NOTE]
> Related: use [public interfaces](References.md#public-interfaces) for the
> commands and import paths users can rely on.
## Configure Local Environment Files
`KNF_WORKERS` controls dataset export, image inspection, and the workspace copy
made by `knf train prepare` and a fresh `knf train start`. It is read from the
selected AppRC storage config and defaults to eight workers. Set it with the TUI
or the non-interactive config command:
```bash
knf --storage demo config set KNF_WORKERS 8 --scope storage
```
OpenAI-compatible image captioning reads `BASE_URL` or `OPENAI_BASE_URL` plus
`OPENAI_API_KEY` or `API_KEY` from `.env` unless you pass `--dotenv-path`:
```bash
cat > .env <<'EOF'
BASE_URL=http://localhost:1234/v1
OPENAI_API_KEY=not-needed
EOF
```
> [!CAUTION]
> Keep secrets and machine-local project roots out of committed files.
## Register A Persistent Storage Root
Use `knf project init` when a training project root should become a switchable
Kneiff project. It creates the Kneiff scaffold, registers the AppRC storage,
and initializes the shallow conventional layout:
```text
demo-project/
├── .env.apprc-storage # Machine-local AppRC values, created on registration
├── .gitattributes # Git LFS tracking rules; does not install Git LFS
├── .gitignore # Ignores local and generated project outputs
├── .git/ # New main-branch Git repository
├── default_tags.txt # Project-owned seed: kneiff + identity tokens
├── vocabulary.knf.yaml
├── prompts.knf.yaml
├── configs/
│ ├── ANIMA.knf.yaml # Sygred Anima training config and local model paths
│ └── F2K_9B.knf.yaml # Flux2 Klein 9B training config
├── SOURCE/
│ ├── 0-FULLBODY/
│ └── 2-HEAD/
├── MANIFEST.knf.xlsx # Editable, created by dataset sync
├── MANIFEST.yaml # Generated Git-review sidecar
├── HF/ # Created empty and ignored
├── TRAINING/ # Created empty and ignored
└── .old_manifests/
```
```bash
knf project init "D:\Training\demo-project" \
--name demo \
--activation-token Character_Token \
--species-token species_token \
--git-user-name kneiff \
--yes
knf project use demo
knf project show
knf project validate
knf project list
```
AppRC uses the application name `knf`. The named-storage index lives at
`~/.config/knf/knf.apprc.toml`, the app-wide dotenv layer lives at
`~/.config/knf/.env.apprc-app`, and `KNF_APPRC_TOML` can override the index
path. Each registered root has a machine-local `.env.apprc-storage`. On
WSL/Linux, Windows drive paths are normalized before Kneiff stores or uses them.
When `vocabulary.knf.yaml` does not exist, `knf project init` requires both
`--activation-token` and `--species-token`. Omit both options when adopting a
root that already has its vocabulary. The command also creates
`configs/ANIMA.knf.yaml`, the Sygred Anima Base v1.0 training template, and
`configs/F2K_9B.knf.yaml`, the fully commented Flux2 Klein 9B template. The
Flux2 config maps `fullbody`, `head`, and `genitals` to their `SOURCE/` folders,
uses `black-forest-labs/FLUX.2-klein-base-9B` with unset component paths, and
keeps the rank-32, batch-2, 320-step training settings, with a 120-step
fullbody/genitals warm-up. The Anima config continues to preserve its configured
local model paths.
`default_tags.txt` is a project-owned seed containing
`kneiff`, the activation token, and the species token. Kneiff does not consume
that file at runtime.
For a new root, the command runs `git init --initial-branch main` and sets the
repository-local `user.name` from `--git-user-name` (default `kneiff`). It
never sets an email, touches global Git config, creates a commit, configures a
remote, or installs Git LFS. Existing repositories retain their Git identity
and remotes. The generated `.gitattributes` declares LFS tracking rules for
images, models, archives, and workbooks, while `.gitignore` ignores
`.env.apprc-storage`, `HF/`, `TRAINING/`, and other machine-local/generated
paths. `HF/` and `TRAINING/` are created empty without placeholder files.
The initializer registers the root in AppRC's `knf.apprc.toml` and AppRC creates
the root `.env.apprc-storage`. Existing project-owned files are never replaced.
It is the only project initializer.
There is no separate `kneiff.project.yaml`. The selected AppRC storage is the
project identity, and Kneiff derives the shallow paths above in code.
`vocabulary.knf.yaml` overlays the packaged generic JTP vocabulary with the
project's activation token, species token, and project-only axes.
Each declared axis requires an explicit `values` list and each value requires a
canonical `tag`. `prompts.knf.yaml` is an optional version-1 project overlay on
Kneiff's packaged core prompt catalog. Package rows provide the portable
character reference, NOOB negative default, and fixed validation controls;
project rows provide project-specific scenes. Every row requires a nonempty
`uses` list such as `showcase`, `training_validation`, or `negative_control`.
Only the core catalog may own `negative_control` rows. Both loaders reject
misspelled or unsupported schema fields. Add project-specific ComfyUI overrides
only when needed as `workflows/showcase_.workflow.json`.
To control positive and negative prefixes during `knf comfy showcase`, set
`defaults.showcase_model_prompts` in `prompts.knf.yaml`. The built-in model
family keys are `anima`, `flux2`, `pony`, `noob`, `z-image`, and `krea2`.
Settings apply only to showcase generation and are not added to
training-validation prompts. A row's `negative_prompt` or
`negative_prompts.` supplies the negative base; the model's
`negative_prefix` prepends once after that selection. `negative_prefix: ""`
deliberately disables only the model prefix. The old model-level
`negative_prompt` key is rejected. Set `ignore_positive_prefix: true` on a row
to submit its raw caption without any showcase positive prefix. A project
workflow named `workflows/showcase_.workflow.json` uses `` as its model
key unless it belongs to a built-in family.
```yaml
defaults:
showcase_model_prompts:
anima:
positive_prefix: "masterpiece, best quality, "
negative_prefix: "worst quality, lowres, "
flux2:
negative_prefix: ""
pony:
positive_prefix: "score_9, score_8_up, score_7_up, "
negative_prefix: "score_4, score_3, score_2, score_1, "
noob:
positive_prefix: "masterpiece, best quality, newest, "
negative_prefix: "worst quality, lowres, "
prompts:
- id: character_reference
uses: [showcase]
negative_prompt: "blurry anatomy"
ignore_positive_prefix: true
captions: {nlg: "A character reference image."}
```
`knf project use NAME` persists the default `KNF_STORAGE` selector in app-wide
AppRC config. A shell `KNF_STORAGE` value or root `--storage NAME_OR_PATH`
option overrides it for a specific command:
```bash
knf --storage demo dataset plan chroma
```
`knf project list` validates every registered root. `knf project show` reports
the selected root, identity, validation status, default Anima and Flux2
configs, default tags, Git files and repository state, starter source
directories, and every conventional project path. `knf project validate` checks
the fixed starter files and source directories, parses both generated configs
through the export and SimpleTuner loaders, then parses the vocabulary, packaged
core catalog, optional project overlay, and workflow overrides and reports their
counts.
An explicit `configs/*.knf.yaml` path must remain inside the selected project
root. Kneiff rejects paths from another project; select that project with
`--storage` instead. Inspect AppRC paths and edit native config values with the
generated commands:
```bash
knf --storage demo config show
knf config paths
knf config doctor
knf --storage demo config edit
```
`knf config edit` opens the Textual TUI. `knf config app init` creates the
app-wide dotenv file; `knf config storage add`, `list`, and `remove` manage the
named-storage index. Use `knf config set KEY VALUE --scope app` or
`--scope storage` for a non-interactive override.
Keep project-local settings such as `KNF_WORKERS` in the project root
`.env.apprc-storage`, managed by AppRC.
The storage-local file cannot select its own root because AppRC must resolve
`KNF_STORAGE` before it knows which file to load. Use `knf project use`, the
shell environment, or root `--storage` for selection.
Set `COMFY_MODELS_DIR` in `.env.apprc-storage`, the shell, or `--env-file` when
using `knf comfy showcase` with LoRA discovery. Root `--env-file` options must
appear before the command group and may be repeated:
```bash
COMFY_MODELS_DIR="/path/to/comfyui-models"
knf --storage demo --env-file ./comfy.env comfy showcase
```
Set `COMFY_LORAS_DIR_1` when the interactive LoRA picker should open in a
specific subdirectory below `$COMFY_MODELS_DIR/models/loras`. `knf comfy
upscale` has built-in defaults, but you can override its model choices when
your ComfyUI install uses different filenames:
```bash
COMFY_LORAS_DIR_1="/path/to/comfyui-models/models/loras/project/version"
COMFY_UPSCALE_MODEL="4x-UltraSharpV2.pth"
COMFY_UPSCALE_REFINER_UNET="krea2_turbo_fp8_scaled.safetensors"
COMFY_UPSCALE_REFINER_CLIP="qwen3vl_4b_fp8_scaled.safetensors"
```
`COMFY_UPSCALE_REFINER_CLIP_TYPE` defaults to `krea2`, and
`COMFY_UPSCALE_REFINER_VAE` defaults to `qwen_image_vae.safetensors`.
If a default is not installed, interactive terminals open a picker from
ComfyUI's reported options; scripts should set the matching `COMFY_*` value,
pass `-z` for the legacy Z-Image quality workflow, or pass `--fast`.
Set LM Studio prompt-generation settings in `.env.apprc-storage`, the shell, or
`--env-file` when using `knf llm prompt`. LM Studio must already be running
with its OpenAI-compatible local server enabled:
```bash
KNF_LMSTUDIO_BASE_URL="http://127.0.0.1:1234/v1"
KNF_LMSTUDIO_MODEL="qwen/qwen3-14b"
KNF_PROMPTGEN_DRAFT_TEMPERATURE=0.7
KNF_PROMPTGEN_REVIEW_TEMPERATURE=0.2
```
Use `KNF_LMSTUDIO_DRAFT_MODEL` and `KNF_LMSTUDIO_REVIEW_MODEL` when the two
passes should use different local models. `KNF_LMSTUDIO_CUDA_DEVICE_NAME`
resolves a visible GPU name fragment, such as `RTX 4070 Ti Super`, for future
Kneiff-managed launch helpers; it cannot change the GPU used by an already
running LM Studio server.
When prompt exchange saving is enabled, `knf llm prompt` writes raw transcripts
under the selected storage root at `.llm_promptgen/`. Set
`KNF_PROMPTGEN_EXCHANGE_DIR` only when you want to override that location.
`knf llm prompt` saves user-facing JSON and review-text prompt results below
`.llm_promptgen/results/` in the selected storage by default. Pass
`--no-export` to skip those result files for one run. The former `--export`
opt-in is removed because export is now the default.
When a command needs a config, pass either an explicit config path or a selector
such as `chroma`, `Chroma`, or `CHROMA`. Selectors match direct child
`configs/*.knf.yaml` files in the active storage root. If no selector is passed,
Kneiff auto-selects only when exactly one config is available. If multiple
configs are available in an interactive terminal, use the arrow-key picker.
Batch-capable commands let you press Space to toggle configs, `a` to toggle all,
and Enter to confirm; single-config commands still select the highlighted config
with Enter. In scripts, pass explicit config paths or selectors so
non-interactive runs keep failing with the available-config list.
# 3. Dataset Workflows
## Sync A Manifest Workbook
Refresh only `MANIFEST.knf.xlsx` and generated `MANIFEST.yaml` from `SOURCE/`:
```bash
knf dataset sync path/to/project/configs/example.knf.yaml --manifest-only
```
With storage selected, use the config selector instead:
```bash
knf dataset sync example --manifest-only
```
Kneiff creates or updates fixed A-H workbook sheets. Every image occupies
exactly 13 rows: `Relative_path`, `Subject`, `Appearance`, `Composition`,
`Pose and behavior`, `Face`, `Anatomy`, `Sexual content`, `Scene`,
`Presentation`, `Fallback`, `SFW`, and `Notes`. The thumbnail in column A and
the derived `tag`, `json`, `nlg`, `chroma`, and `prose` outputs in columns D-H
are merged across the block. Each column-C `User Input` cell remains
independent.
`MANIFEST.yaml` is generated beside the workbook so Git can show manifest
changes clearly. Keep editing `MANIFEST.knf.xlsx`; the YAML sidecar is rewritten
from the workbook sync output.
When `configs/*.knf.yaml` files are present, run a dataset sync:
```bash
knf dataset plan path/to/project/configs/example.knf.yaml
knf dataset sync path/to/project/configs/example.knf.yaml
knf dataset sync alpha beta
```
Sync stages and validates `MANIFEST.knf.xlsx`, schema-version-2
`MANIFEST.yaml`, and a legacy project vocabulary before activating any of
them. The sidecar stores logical records and user input but no derived
captions. A first migration archives the previous file set below
`.old_manifests/` and restores it if activation fails. Existing
`_resolution_plan` sheets remain available; the retired `_caption_previews`
sheet is replaced by the five merged output columns.
The selected AppRC storage defines the project root. The direct-child
`configs/.knf.yaml` supplies the config id and workflow settings; source
images are read from `SOURCE/`, manifest rows stay relative to `SOURCE/`, and
the export root is `HF//`. Each exported dataset also gets a
`kneiff-training-image-grid.jpg` contact sheet, Hugging Face `metadata.jsonl`,
a generated dataset-card `README.md`, and publish-safe `.hfignore` and
`.gitignore` files in its export root. SimpleTuner files are generated by
`knf train prepare`, not by dataset sync.
> [!NOTE]
> Related: use [manifest-to-export model](Explanations.md#manifest-to-export-model)
> for the ownership boundary between source images, workbook rows, and exports.
## Sync A Training Dataset
Use a `configs/*.knf.yaml` file to map source folders to export subsets:
```yaml
mappings:
identity:
- "0-IDENTITY"
details:
- "1-DETAILS"
image_resize:
min_pixel_area: 768
max_pixel_area: 1536
augmentations:
mirrored_extra: true
mirrored_transform: flip_only
seed: 12345
export_sfw_subset: true
publishing:
huggingface:
repo_id: account/dataset-name
pretty_name: Example LoRA Dataset
version: v1.0
optimized_for_model: Chroma1-HD
license: cc-by-4.0
tags: [image-captioning, diffusion-training, lora]
provenance: TODO
adult_content: false
notes: TODO
```
Keep filesystem paths out of this file. The config must live directly at
`/configs/.knf.yaml`; Kneiff derives `SOURCE/`,
`MANIFEST.knf.xlsx`, `HF//`, and numbered `TRAINING/` paths. The keys
`source_root`, `manifest_path`, `export_root`, and
`allow_export_inside_source` are rejected.
`augmentations.mirrored_extra` adds fixed mirrored image/caption pairs during
export. Use `mirrored_transform: flip_only` for exact horizontal mirrors, or
`mirrored_transform: augmented` for the legacy mirror plus kneifftools crop,
rotation, and color jitter. SimpleTuner runtime crop settings live separately
under `training.simpletuner.dataset`.
Inspect the resolved paths before writing:
```bash
knf dataset plan path/to/project/configs/example.knf.yaml
knf dataset plan example
knf dataset plan alpha beta
```
The plan output uses `Config: ` for the selected CONFIG identity and
`Config path: ...` for the resolved `configs/.knf.yaml` file.
Sync from the explicit config:
```bash
knf dataset sync path/to/project/configs/example.knf.yaml
knf dataset sync example
knf dataset sync alpha beta
```
Successful syncs write `kneiff-training-image-grid.jpg` in the export root so
you can quickly inspect the non-mirrored training images by subset. Sync also
writes Hugging Face `metadata.jsonl`, a generated `README.md`, `.hfignore`, and
`.gitignore` files. Metadata rows use relative `file_name` values and follow the
configured SimpleTuner training subsets when training is enabled. Exported image
and caption files are diffed against `HF//.kneiff-export-state.json`, so
unchanged files are skipped while README and metadata artifacts still refresh.
If that state file is missing or invalid, the next sync rewrites planned files
and preserves files outside the current plan. Use `--rebuild` when you
explicitly want to clear the export root.
Regenerate only the dataset card from existing export artifacts:
```bash
knf dataset readme path/to/project/configs/example.knf.yaml
```
Regenerate only that non-mirrored grid from the existing export directory:
```bash
knf dataset grid path/to/project/configs/example.knf.yaml
knf dataset grid path/to/project/configs/example.knf.yaml --output /tmp/training-grid.jpg
```
When SimpleTuner training is enabled, `training.simpletuner.subsets` must be a
non-empty explicit mapping. Sync reports include `Train prob` and `Train %`
columns. `Train %` matches the configured
`data_backend_sampling` mode: `auto-weighting` uses exported image count times
probability, while `uniform` uses probability only. The generated `sfw` subset
is excluded unless it is explicitly listed under `training.simpletuner.subsets`.
Sync also reports source and planned export resolution buckets plus a
source-resolution plan showing Kneiff export processing and predicted SimpleTuner
behavior from the current config. The report starts with kept vs `KICKED`
health counts and prints a kicked-out image table whenever any input would be
filtered by the trainer. Oversized inputs stay kept when SimpleTuner can
downsample them through `maximum_image_size` and `target_downsample_size`;
`KICKED` is reserved for true trainer filters such as `minimum_image_size`,
`minimum_aspect_ratio`, `maximum_aspect_ratio`, unsupported crop prediction, or
an unconfigured backend. This matches SimpleTuner's upstream
[`minimum_image_size`](https://github.com/bghira/SimpleTuner/blob/main/documentation/DATALOADER.md#minimum_image_size),
[`maximum_image_size` and `target_downsample_size`](https://github.com/bghira/SimpleTuner/blob/main/documentation/DATALOADER.md#maximum_image_size-and-target_downsample_size),
and
[option](https://github.com/bghira/SimpleTuner/blob/main/documentation/OPTIONS.md)
documentation. Resolution tables always include totals and preserve the smallest
buckets before aggregating `other`. Real sync runs list changed image files with
their source size, output size, and resize scale; add `--verbose` to show all
changed images and all uncapped resolution buckets.
Use `--dry-run` to validate and count outputs without writing files:
```bash
knf dataset sync path/to/project/configs/example.knf.yaml --dry-run
```
Use `knf dataset describe` when you want the same dry-run counts table without
the sync summary or any file writes:
```bash
knf dataset describe path/to/project/configs/example.knf.yaml
```
Use `--rebuild` only when you are ready to delete and recreate generated public
export files. Add `--yes` to confirm that rebuild prompt in unattended runs:
```bash
knf dataset sync path/to/project/configs/example.knf.yaml --rebuild
knf dataset sync path/to/project/configs/example.knf.yaml --rebuild --yes
```
> [!WARNING]
> `--rebuild` deletes existing contents under the config-derived export root.
> Review `HF//` before confirming a destructive rebuild.
## Configure Caption Outputs
Use `caption_outputs` for global sidecar defaults and
`caption_outputs_overrides` for per-subset changes:
```yaml
caption_outputs:
mode: hybrid_txt
formats: [tags, natural]
tag_scope: supplemental
caption_outputs_overrides:
identity:
formats: [tags, natural, json]
sfw:
mode: separate_txt
formats: [tags, natural, chroma, nlg]
caption:
subject_sex: null # Set male or female only when the project needs it.
```
Supported caption formats are:
| Format | Output |
|---|---|
| `tags` | Kneifftags tag profile. |
| `natural` | Kneifftags natural-language profile. |
| `json` | Compact category-to-tag JSON derived from the shared analysis. |
| `nlg` | Kneifftags controlled NLG profile. |
| `chroma` | Kneifftags Chroma profile. |
| `hybrid` | Kneifftags hybrid natural-and-tag profile. |
Only the listed format names are accepted. Replace removed `tag` and `prose`
config values with `tags` and `natural`. The workbook headers remain `tag` and
`prose` because they are part of the fixed workbook layout.
Every caption for one image is rendered from the same Kneifftags analysis.
Unknown inputs are preserved in fallback output and reported as warnings.
Category mismatches and ambiguous inputs also remain visible in diagnostics.
Active Kneifftags errors block migration or export.
> [!WARNING]
> Rows marked `SFW` reject explicit or NSFW labels, explicit anatomy, penis
> state and appearance, sexual actions, sexual fluids, and the exact tags
> `anus`, `balls`, and `genitals`.
Set `tag_scope: supplemental` to keep identity and tag-only facts while omitting
tags already expressed in natural text inside a joined caption. Set
`tag_scope: all` when you need the full tag tail. Standalone `tags` and
`chroma` files always use the complete tag set.
Use `formats: [nlg]` for Flux2/Z-Image Qwen-style character LoRA captions.
Caption-text compatibility does not make LoRA weights cross-model-compatible;
train and load LoRAs within the target model family.
Project-only tags from `vocabulary.knf.yaml` are entered in the matching fixed
workbook field. Use `Fallback` when no fixed semantic field applies. A project
extension can define categories, groups, aliases, output tags, natural/NLG
phrases, and safety metadata; Kneifftags owns their validation and rendering.
Project character and species defaults are namespace-qualified so a built-in
tag with the same spelling cannot replace project identity.
> [!IMPORTANT]
> `caption_outputs` no longer accepts per-subset mappings. Put subset-specific
> changes under `caption_outputs_overrides`.
# 4. LoRA Workflows
## Write SimpleTuner Artifacts
LoRA training settings live under `training.simpletuner` in the same
`configs/*.knf.yaml` file used for dataset sync.
List prepared and completed training runs:
```bash
knf train runs example
knf train runs /abs/path/to/project/configs/example.knf.yaml
```
Prepare the generated SimpleTuner JSON files without launching:
```bash
knf train prepare example
knf train prepare alpha beta --testrun
knf train prepare /abs/path/to/project/configs/example.knf.yaml
```
> [!IMPORTANT]
> Run `knf dataset sync CONFIG` before preparing training artifacts. `knf train
> prepare` requires a populated `HF//` export with files in every
> enabled `training.simpletuner.subsets` entry, and it does not run dataset sync
> or create an empty export root. There is no project-specific subset fallback.
The config-derived export root stays the public dataset folder. `knf train
prepare` writes SimpleTuner state under `TRAINING/_/`, including
`dataset/`, `simpletuner-config.json`, `simpletuner-multidatabackend.json`,
`_simpletuner-output/`, and `kneiff-training-run.json`. Generated JSON reserves
paths below `.simpletuner-cache/`; SimpleTuner creates cache content when it
runs. When validation prompts are enabled, preparation also writes
`simpletuner-validation-prompts.json`. These are Kneiff-owned fixed paths;
training configs cannot rename or redirect them. Fresh preparation chooses the
next free run number by scanning existing `TRAINING/_/`
workspaces and root validation-grid files. Training preparation and review
output include a SimpleTuner-only resolution health summary for the copied
dataset, including kicked-out image paths when any copied image would be
filtered; random aspect crop modes list possible buckets instead of exact
counts. `knf train start` may create `.kneiff-simpletuner/` later when the
launcher bootstraps model conversions. That directory and its generated
component directories and metadata files must not be symlinks. The only
generated link is `chroma-text-encoder/text_encoder/model.safetensors`, and an
existing link is accepted only when it still points to the configured Chroma
text encoder.
Every numbered workspace must contain a valid `kneiff-training-run.json`, and
its dataset, JSON artifacts, cache, prompt library, and output paths must match
the fixed locations in that workspace. The marker requires `schema_version: 1`,
normalized absolute paths, explicit typed optional fields, exact SHA-256
digests, and a nonempty unique subset list matching direct `dataset/` children
from the generated data-backend JSON. Kneiff does not coerce, backfill, or
rewrite pre-layout training runs. Archive or remove an unsupported workspace
and prepare a new run.
Use `training.simpletuner.curriculum` when different subset mixes should train
at different points inside the same run. Phase steps are included in
`training.simpletuner.trainer.max_train_steps`; this example trains the
`identity` subset for the first 400 steps and starts every configured image
subset at step 400:
```yaml
training:
simpletuner:
curriculum:
enabled: true
phases:
- name: focused_start
start_step: 0
subsets: [identity]
- name: full_mix
start_step: 400
subsets: all
```
Apply a short smoke-test profile from `training.simpletuner.trainer_testrun`:
```bash
knf train prepare path/to/project/configs/example.knf.yaml --testrun
```
Delay scheduled intermediary validation until an exact optimizer step with
`training.simpletuner.validation_schedule.start_step`. With a start step of 550
and `trainer.validation_step_interval: 100`, it renders at steps 550, 650, 750,
and so on. The step-0 base-model benchmark and the end-of-run validation remain
available. Omit this setting or use `start_step: 0` to retain SimpleTuner's
normal interval schedule. A delayed schedule requires a positive step interval
and cannot be combined with `trainer.validation_epoch_interval`.
Kneiff saves the chosen step in the prepared run, so reviews, resumes, and
extensions keep that schedule even if the source YAML is changed later.
```yaml
training:
simpletuner:
validation_schedule:
start_step: 550
trainer:
validation_step_interval: 100
```
When `training.simpletuner.validation_prompts` is configured, `knf train
prepare` writes the fixed `simpletuner-validation-prompts.json` beside the
generated SimpleTuner config and points `user_prompt_library` at that file. The
clean wrapper supports direct custom prompts plus manifest-generated prompts in
one library.
Use `custom` for prompt text that should be copied directly into the prompt
library, `from_prompts` for curated captions from the selected project's
resolved core-plus-overlay catalog marked with `training_validation`, and
`from_manifest` for an activation prompt plus one sampled prompt per configured
export subset. `default_negative_controls` independently adds the fixed wolf
and two fixed human controls and is enabled by default. Set root `styles` once
to supply every enabled source; each source may override it with
`caption_styles`. Controls and manifest sampling require exactly one effective
style. Training config cannot redirect the project prompt source.
Custom prompts may include `{activation_token}`, which resolves to the selected
project's character token when the prompt library is written.
Prompt catalogs live under `kneiff.prompts`; SimpleTuner validation artifacts
live under `kneiff.training.lora`. Use `positive_prefix` to prepend
model-specific quality or safety tags to every generated and custom validation
prompt. Packaged controls and manifest sampling support `tags`, `natural`,
`chroma`, and `nlg`.
Generated `sfw` fanout rows are not added as
validation prompts unless `sfw` is an explicit mapping subset.
```yaml
training:
simpletuner:
validation_prompts:
styles: [nlg]
positive_prefix: "masterpiece, best quality, score_7, safe, "
custom:
custom_portrait: "{activation_token}. A close-up portrait validation prompt."
from_prompts: {}
```
Add `from_manifest` with `caption_styles: [tags]` when sampled dataset prompts
are also needed. Set `default_negative_controls.enabled: false` to omit the
packaged control suite. The generated section contains fixed wolf,
residential-street, and office-worker negative controls, an activation control,
and one subset prompt per mapping.
Project prompt entries are ordered after core rows by prompt row, caption style,
and caption variant; generated prompt keys receive a runtime index prefix for
stable file explorer ordering. `negative_control_species` is not configurable.
Kneiff ships LoRA config templates as package resources under
`kneiff.training.lora.templates`: `complete.yaml`, `chroma.yaml`,
`z-image.yaml`, `flux2-klein-4b.yaml`, `flux2-klein-9b.yaml`, `anima.yaml`,
and `sdxl.yaml`. The
complete template is a reference for supported settings, `sdxl.yaml` is a full
commented Pony/SDXL starting config, and the remaining model-specific templates
are compact overlays with only model-specific caption, model, validation, and
crop recommendations.
> [!NOTE]
> Related: use [LoRA workflow model](Explanations.md#lora-workflow-model) for
> how Kneiff translates one dataset config into SimpleTuner artifacts.
## Launch SimpleTuner Training
Launch SimpleTuner from the generated artifacts:
```bash
knf train start example --cuda-device 1
knf train start alpha beta --yes
knf train start /abs/path/to/project/configs/example.knf.yaml --cuda-device 1
```
Set `KNF_SIMPLETUNER_EXECUTABLE` in the app-wide `.env.apprc-app` file when
Kneiff should use a specific SimpleTuner environment instead of the first
`simpletuner` command on `PATH`:
```dotenv
KNF_SIMPLETUNER_EXECUTABLE=~/repos/SimpleTuner/.venv/bin/simpletuner
```
The configured value must be an absolute path or begin with `~`, point to an
existing console script, and declare its Python interpreter directly in its
shebang. An unset or blank value preserves the `PATH` lookup. Kneiff uses the
selected interpreter to start the workspace's copied patch runner in an
isolated child process. It never imports SimpleTuner into the Kneiff process or
temporarily replaces the Kneiff process's working directory and environment.
Interactive terminals show a review menu before launch. Use `json` to inspect
the generated SimpleTuner files, `diff` to compare against the previous run, and
`start` to launch. Run-index-only path changes inside `TRAINING/_/`
are hidden from the diff so real generated-config changes are easier to spot.
Use `--yes` or `--no-review` when running unattended.
After a successful run, Kneiff scans SimpleTuner's `validation_images`, keeps
only the trained-model half of paired split validation outputs, leaves the
first-run single-image renders intact, and writes
`TRAINING/_-kneiff-validation-progress-grid.jpg`.
When prompt-library metadata is available, each validation column shows the
prompt key plus up to ten wrapped lines of full prompt text at both the top and
bottom of the grid.
For SDXL/Pony LoRAs, the same successful run also writes
`pytorch_lora_weights.comfyui.safetensors` beside each
`pytorch_lora_weights.safetensors` checkpoint so ComfyUI can load the exported
adapter directly.
Regenerate only that grid from existing validation images:
```bash
knf train grid path/to/project/configs/example.knf.yaml 1
knf train grid path/to/project/configs/example.knf.yaml 1 --output /tmp/validation-grid.jpg
```
Run the testrun profile and regenerate artifacts first:
```bash
knf train start example --testrun --cuda-device 1
```
Resume a prepared or incomplete run without regenerating artifacts:
```bash
knf train start example --resume 1 --cuda-device 1
knf train start alpha beta --resume 2
knf train start alpha --resume
knf train start /abs/path/to/project/configs/example.knf.yaml --resume
```
> [!IMPORTANT]
> `--resume` launches existing generated JSON files from the selected run
> workspace. It does not regenerate artifacts from the current YAML config.
> The workspace-local `dataset/` copy must still exist and contain files for
> every active image backend. If it is missing or stale, run
> `knf dataset sync CONFIG` and prepare a new run.
> Pass a run number to skip the interactive prompt. Bare `--resume` opens an
> interactive picker for `not_started` and `incomplete` runs.
> Running `knf train start` without a selector in an interactive terminal first
> lists those resumable runs, then the configs that would start new runs.
Use `--fallback-cuda-device` when the launch wrapper should record a fallback
device for the child process environment:
```bash
knf train start path/to/project/configs/example.knf.yaml --cuda-device 1 --fallback-cuda-device 0
```
## Showcase Training Checkpoints
Set `COMFY_MODELS_DIR` to the ComfyUI models root. Its `models/loras`
directory must be writable:
```bash
COMFY_MODELS_DIR="/path/to/comfyui-models"
```
Open the shared run-first picker for numeric checkpoint files from direct
training-run `_simpletuner-output` trees:
```bash
knf comfy showcase -t
```
Pass one or more positive step numbers to include only exact checkpoint
directories. Every requested step must exist in at least one run:
```bash
knf comfy showcase -t 400
knf comfy showcase -t 400 800 --workflow anima
```
The picker first lists direct `TRAINING/_/` directories. Opening
a run shows only its `checkpoint-` LoRAs. `../ Other TRAINING runs`
returns to the run list without losing checked files, so a showcase can compare
several runs. Root-level final exports are hidden because they have no numeric
checkpoint step. ComfyUI-native `*.comfyui.safetensors` files appear first,
followed by every remaining `.safetensors` file. Kneiff excludes caches, dataset
copies, symlinks, and files outside direct `_simpletuner-output` trees.
Use Space to toggle LoRAs or `a` to select all candidates. Enter selects the
checked LoRAs, or only the highlighted LoRA when nothing is checked. Each
selected file is hard-linked into a unique directory below
`models/loras/.kneiff-training/` when possible and copied when the two paths use
different filesystems. Kneiff removes those temporary directories after the run,
including when workflow resolution or showcase generation fails. Omit
`--workflow` to infer the shared workflow from the first selected
training-relative path and open the workflow picker when the path has no unique
match.
The run and file lists keep the active row in a terminal-height-bounded
viewport. Above/below indicators show omitted rows, and each rendered row is
kept to one terminal line so arrow-key navigation replaces the prior frame
instead of flooding a short terminal.
Kneiff prints each prompt source on its own row, followed by the inspected
workflow settings and the dated ComfyUI output directory. Interactive terminals
show one progress bar across every selected LoRA and eligible prompt.
Every successful showcase downloads the reported images, assembles one
timestamped JPEG in a temporary directory, and uploads it into the same dated
ComfyUI `output` subfolder as the individual `SaveImage` results. Prompts are
the columns and LoRAs are the rows, so selecting several checkpoints produces
one comparison grid. Prompt IDs and up to ten wrapped lines of full prompt text
appear above and below the images. Kneiff does not persist showcase grids below
the project's `TRAINING` directory.
> [!IMPORTANT]
> `-t` requires an interactive terminal and cannot be combined with `--lora`.
> `COMFY_MODELS_DIR` must describe the filesystem used by the running ComfyUI
> server.
## Promote A Training Checkpoint
Install one reviewed numeric checkpoint as a release copy below the configured
ComfyUI LoRA library:
```bash
knf train promote
knf train promote --name Rook-Preview
```
`knf train promote` uses the same run-first TRAINING picker, then lets you
browse existing directories under `$COMFY_MODELS_DIR/models/loras`. It asks for
a mandatory release version in `v.` form, shows the exact target,
and requires confirmation before copying. The target name is:
```text
--v.-.safetensors
```
`name` comes from `custom_tokens.character.text` unless `--name` / `-n`
overrides it. `MODEL_ID` is the uppercase compact model ID inferred from the
generated `simpletuner-config.json`, and `step` comes from the selected
`checkpoint-` directory. The source in `TRAINING` is never moved or
overwritten; an existing destination filename is refused.
# 5. Image Workflows
## Generate Solo And Duo T2I Runs
Run LM Studio's OpenAI-compatible server and ComfyUI, then configure the
filesystem and model paths in the selected project's `.env.apprc-storage`:
```bash
COMFY_MODELS_DIR="/path/to/comfyui-models"
COMFY_LORAS_DIR_1="project/primary"
COMFY_LORAS_DIR_2="project/partner"
COMFY_T2I_MODEL_SOLO="diffusion_models/Krea2/krea2_turbo_fp8.safetensors"
COMFY_T2I_LORA_SOLO="primary-krea2.safetensors"
COMFY_I2I_MODEL_SOLO="diffusion_models/F2K_9B/flux-2-klein-9b.safetensors"
COMFY_I2I_LORA="primary-flux2.safetensors"
COMFY_T2I_MODEL_DUO="diffusion_models/Anima/anima-base-v1.0.safetensors"
COMFY_T2I_LORA_DUO_1="primary-anima.safetensors"
COMFY_T2I_LORA_DUO_2="partner-anima.safetensors"
COMFY_I2I_MODEL_DUO="diffusion_models/F2K_9B/flux-2-klein-9b.safetensors"
COMFY_I2I_LORA_1="primary-flux2.safetensors"
COMFY_I2I_LORA_2="partner-flux2.safetensors"
```
Strength keys default to `1.0`. Add
`COMFY_T2I_LORA_STRENGTH_SOLO`,
`COMFY_I2I_LORA_STRENGTH`,
`COMFY_T2I_LORA_STRENGTH_DUO_1`,
`COMFY_T2I_LORA_STRENGTH_DUO_2`,
`COMFY_I2I_LORA_STRENGTH_1`, or
`COMFY_I2I_LORA_STRENGTH_2` when a model needs another value.
For duo runs, declare participant 2 in the project vocabulary. The canonical
`custom_tokens.character` entry remains participant 1:
```yaml
additional_activation_tokens:
- Sygred_Lightfeet
```
When `--pipeline` is omitted, `knf comfy t2i solo` opens an arrow-key picker
for Krea2 only, Krea2 plus Flux2 Klein cleanup, or Anima plus Flux2 Klein
cleanup. Non-interactive scripts must pass the choice. Krea2 only produces nine
finals by default; either Flux profile and duo produce nine baselines plus 27
cleanup candidates:
```bash
knf comfy t2i solo --pipeline krea2 "full-body portrait in a sunlit workshop"
knf comfy t2i solo --pipeline anima-flux2 "full-body portrait in a sunlit workshop"
knf comfy t2i duo "two characters talking beside a forest stream"
```
Use `--num-prompts`, `--num-images`, and `--num-cleanups` on Flux profiles or
duo to change branch counts. Solo Flux cleanup accepts `--i2i-model`,
`--i2i-lora`, and `--i2i-lora-strength` for one run. Krea2-only rejects those
cleanup options. `--seed` controls image branches only; LM prompt text remains
sampled.
Before each pass, Kneiff derives project identity facts, project-specific visual
concepts, and scene-relevant curated prompt references from the selected
vocabulary and prompt catalog. The LLM uses references only for persistent
identity and prompt grammar, not their scene-specific content. Every run prints
its random or explicit root seed and records the derived context plus exact raw
and effective prompts in readable `run.knf.yaml`.
> [!IMPORTANT]
> Kneiff sends run-relative output paths to ComfyUI and uploads
> `run.knf.yaml` through the same server-managed output mechanism used by the
> showcase grid. No client-side ComfyUI output path is required. Set optional
> `COMFY_OUTPUT_DIR` or pass `--output-dir` only to keep an additional local
> mirror. Any Flux cleanup also requires `qwen_3_8b_fp8mixed.safetensors` below
> ComfyUI's text encoders and `full_encoder_small_decoder.safetensors` below
> its VAE models, matching the current official distilled 9B edit workflow.
All baseline prompts are queued before Flux cleanup begins. Ctrl-C deletes only
pending prompt IDs created by this run and interrupts the active prompt only
when ComfyUI confirms that it belongs to the same run. Completed files and the
manifest remain available after failure or interruption. Krea2-only prints
`Cleanup: not selected`.
> [!WARNING]
> LM Studio and ComfyUI may allocate GPU memory concurrently. An OOM can come
> from either server; the command prints both URLs before generation starts.
## Convert PNG Files To JPEG
Convert every PNG below a directory into an output subdirectory:
```bash
knf img png2jpg ./images --out-subdir jpg --quality 95
```
Preview without writing files:
```bash
knf img png2jpg ./images --dry-run
```
## Prepare Image Sets
Concatenate images horizontally:
```bash
knf img concat ./a.png ./b.png --output contact-sheet.png --width 2000
```
Rename files with an enumerated random suffix:
```bash
knf img rename ./images/*.png --base-name sample --mode enum --start 1
```
Upscale one file or a directory:
```bash
knf img upscale ./images --out-dir ./upscaled
```
Interactive `png2jpg` and local-upscale batches replace repetitive success
lines with one Rich progress bar. Local upscale starts with a spinner while it
resolves and loads model weights, then adds the discovered image count.
Redirected execution keeps the existing plain per-file output.
> [!WARNING]
> Upscaling may download model weights and can use significant GPU memory.
## Tag And Caption Images
Print e621-style tags from RedRocket/JTP-3:
> [!IMPORTANT]
> The current RedRocket/JTP-3 `main` snapshot requires the Python package
> `pyvips` and native `libvips`. Install Python dependencies with `uv sync` or
> `.venv/bin/python -m pip install -e .`. On Fedora WSL, install the native
> package with `sudo dnf install vips`. Current `main` uses calibrated upstream
> tag selection; `--threshold` is only for legacy pinned revisions.
```bash
knf img tag ./images --recursive
```
For one image, `knf img tag` prints only the selected tag line. For multiple
images or directory inputs, each line is `PATHTAGS`.
Write `.txt` tag sidecars instead:
```bash
knf img tag ./images --recursive --txt
```
Use legacy comma-separated tag text when another workflow expects it:
```bash
knf img tag ./images --recursive --txt --comma
```
> [!NOTE]
> `--csv-stdout` is a probability CSV export mode, not selected-tag text output.
Common `knf img tag` options:
| Option | Use |
|---|---|
| `--txt` | Write `.txt` sidecars next to images instead of printing selected tags. |
| `--comma`, `-c` | Use legacy comma-separated selected-tag text. Without this, tags are e621-style whitespace-separated tokens. |
| `--csv-stdout` | Print probability CSV output from JTP-3. Do not combine it with `--txt` or `--comma`. |
| `--threshold`, `-t` | Set the symmetric tag threshold for legacy pinned JTP-3 revisions. Current `main` uses calibrated upstream tag selection and rejects non-default thresholds. |
| `--device`, `-d` | Select the Torch device, for example `cuda`, `cuda:1`, or `cpu`. |
| `--batch-size`, `-b` | Set images per inference batch. |
| `--workers`, `-w` | Set upstream image-loader workers. Omit it for JTP-3's automatic default. |
| `--seqlen`, `-S` | Set NaFlex sequence length. The default is `1024`; JTP-3 accepts `64` to `2048`. |
| `--prefix`, `-p` | Force tag text to the beginning of selected-tag output. |
| `--repo-id`, `--revision` | Use a different Hugging Face model repository or pinned revision. |
Caption one image through an OpenAI-compatible server:
```bash
knf img caption ./image.png --server --model-name local-model
```
Caption a directory and write sidecars:
```bash
knf img caption ./images --server --model-name local-model --out-suffix .cap.txt
```
Directory captioning counts only images eligible after the existing-sidecar and
`--overwrite` policy. Caught server or image-open failures still advance the
attempt count and remain visible as diagnostics. Full server instruction dumps
are hidden behind an interactive bar but remain present when stdout is
redirected. Single-image captioning does not create a progress display.
`knf img concat`, `knf img rename`, and `knf img tag` do not show Kneiff bars.
Concatenation and renaming finish as short result-oriented operations; JTP-3 is
one upstream subprocess and does not expose reliable per-image completions.
Use the BLIP/Qwen path instead of a local server:
```bash
knf img caption ./images --blip --model-name Qwen/Qwen2-VL-2B-Instruct
```
Generate a two-pass image prompt through LM Studio:
```bash
knf llm prompt "Character_Token resting against a tree, rear view, looking back"
```
`knf llm prompt` prints a human review view by default with the first version,
second version, and review comments. Use `--verbose` or `--format json` for the
full structured JSON payload with dataset-shaped fields, final prompt text,
assumptions, missing input, review issues, and model ids. Use `--text` for a
copy-friendly prompt:
```bash
knf llm prompt "front-view portrait, smiling" --text
```
The explicit review format is also available when scripts should spell out the
human output mode:
```bash
knf llm prompt "front-view portrait, smiling" --format review
```
Prompt results are saved automatically below `.llm_promptgen/results/` in the
selected storage. Suppress those files when you only want terminal output:
```bash
knf llm prompt "front-view portrait, smiling" --no-export
```
Force known fields with repeated `--field FIELD=VALUE` options:
```bash
knf llm prompt "resting against a tree" \
--field character=Character_Token \
--field view=rear_view \
--field pose_body=standing,leaning
```
# 6. Maintainer Checks
## Sync Dependencies
Use `just sync` to install the full maintainer environment from `uv.lock`:
```bash
just sync
```
Use plain `pip` when you only need the package and do not want `uv`:
```bash
.venv/bin/python -m pip install -e "."
```
> [!NOTE]
> Related: use [configuration model](Explanations.md#configuration-model)
> for why runtime installs and maintainer installs are documented separately.
## Run Tests
Run the focused CLI smoke tests:
```bash
.venv/bin/pytest tests/test_cli_smoke.py
```
Run the usual quality tools before finishing a Python code change:
```bash
.venv/bin/ruff format .
.venv/bin/ruff check .
.venv/bin/pyright
.venv/bin/pytest
```
> [!NOTE]
> Related: use [Development: verification](Development.md#verification) for
> the maintainer checklist before a commit.
# 7. Troubleshooting
## Environment Problems
Check the active Python and import location first:
```bash
python --version
python -c "import sys; print(sys.executable)"
python -c "import kneiff; print(kneiff.__file__)"
```
If `knf` is missing, reinstall from the Kneiff repository root:
```bash
.venv/bin/python -m pip install -e "."
```
For image captioning, confirm the local `.env` contains `BASE_URL` or
`OPENAI_BASE_URL`.
> [!NOTE]
> Related: use [failure model](Explanations.md#failure-model) for the normal
> order of checks when a command behaves differently across machines.
## Command Problems
When a `just` recipe fails:
1. Run `just --list`.
2. Run the underlying command manually.
3. Check whether the virtual environment is active.
4. Check whether the command exists in `.venv/bin`.
```bash
just --list
ls .venv/bin
```
> [!NOTE]
> Related: use [command reference](References.md#command-reference) for the
> expected commands and their owners.
## Manifest And Config Problems
When dataset sync fails:
1. Confirm the config file is named `configs/.knf.yaml`.
2. Confirm the manifest uses the canonical `Relative_path` column.
3. Confirm `mappings` names source folders relative to `SOURCE/`.
4. Confirm `caption_outputs_overrides` names only configured subsets, plus
`sfw` when `export_sfw_subset: true`.
5. Confirm YAML keys are unique. Duplicate keys are rejected, including nested
keys such as `training.simpletuner.trainer.caption_dropout_probability`.
6. Run sync in `--dry-run` mode.
```bash
knf dataset sync path/to/project/configs/example.knf.yaml --dry-run
```
> [!NOTE]
> Related: use [configuration files](References.md#configuration-files) for the
> exact file owners and [caption sidecar model](Explanations.md#caption-sidecar-model)
> for output behavior.