Configurable fine-tuning API (TrainingParameters + Optimizer)#289
Merged
Conversation
Wire llama.cpp's ggml-opt training path into the JNI layer, mirroring upstream
examples/training/finetune.cpp: load a model, tokenize a text corpus into a
ggml-opt dataset, run llama_opt_init + llama_opt_epoch for N epochs, and write
the fine-tuned GGUF via llama_model_save_to_file.
- train_engine.{h,cpp} - self-contained native finetune(), independent of the
inference server_context (loads its own model + context; forces no-mmap and an
f32 KV cache, as training requires)
- LlamaTrainer - minimal Java entry point (static finetune(...) overloads)
- CMakeLists.txt - compile train_engine.cpp into libjllama
The ggml-opt / llama_opt symbols already link into the static libjllama with no
build-system change (verified with nm), so this is pure JNI + C++ wiring. The
finetuneNative symbol is exported, the library links and loads cleanly, and the
Java layer compiles through the strict Error Prone / NullAway pipeline.
Scope is deliberately a proof of concept: full-model fine-tuning is compute- and
memory-intensive and upstream training support is experimental. The actual
training run is exercised by a model-gated integration test that self-skips
unless -Dnet.ladenthin.llama.train.model is set. A richer FineTuner API (dataset
handling, optimizer / LoRA options, progress callbacks) can build on this base.
Build a real fine-tuning surface on top of the training POC: replace the fixed-arg LlamaTrainer.finetune with a TrainingParameters builder serialized as JSON across the JNI boundary (the same idiom as ModelParameters / InferenceParameters). Exposes the training knobs the ggml-opt path supports: - corpus from inline text OR a file (read natively) - optimizer selection (AdamW / SGD) via the Optimizer enum - learning-rate schedule (lr0, lr_min, decay_epochs, weight_decay) - validation split, context size, GPU layers, logical/physical batch sizes train_engine parses the JSON config, applies the knobs to common_params, and otherwise runs the same llama_opt_init / llama_opt_epoch loop as the POC. Verified: the native library builds and links against b9842, finetuneNative is exported, and the library loads; TrainingParameters builder->JSON unit tests pass (3); the model-gated integration test now drives the run through TrainingParameters and self-skips without a model. Progress callbacks (native->Java per-epoch loss) are the planned next step: the ggml_opt_epoch_callback has no userdata slot, so that needs a thread-local trampoline and is kept out of this change until it can be exercised end to end.
9d45262
into
bernardladenthin:main
41 of 47 checks passed
bernardladenthin
pushed a commit
that referenced
this pull request
Jul 2, 2026
Brings the merged LlamaTrainer / TrainingParameters fine-tuning feature (#289) onto the llama/ reactor structure. Git's directory-rename detection correctly followed the src/ -> llama/src/ move: the 7 new training files (train_engine.{cpp,h}, LlamaTrainer.java, args/Optimizer.java, parameters/TrainingParameters.java, and the two tests) were placed under llama/src/... and accepted there; llama/CMakeLists.txt auto-merged to keep the `src/main/cpp/train_engine.cpp` source line (relative to llama/, so it compiles as part of the core module). No root src/ or root CMakeLists.txt left behind. Verified locally: reactor Java build green (`mvn -pl llama -am install` — the new training files compile under Error Prone/NullAway + Lombok); TrainingParametersTest (3 model-free tests) passes. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rt1paYztGJ2AKUuBuAGDXE
bernardladenthin
pushed a commit
that referenced
this pull request
Jul 2, 2026
…README - TrainingParameters: keep class-level Lombok @builder @Getter but satisfy the strict javadoc build (failOnWarnings) — replace the raw-source-invisible {@link #builder()} with {@code builder()} and add an explicit private all-args constructor so javadoc sees a real constructor (no synthetic-default warning); Lombok generates the builder around it. Verified: compile + javadoc:jar clean, TrainingParametersTest (3) green. - publish.yml: re-root the Java test jobs' crash-dump/surefire upload globs to llama/ (hs_err_pid*, *.hprof, target/surefire-reports/*) now that surefire runs against -f llama/pom.xml; core.* stays at workspace root (absolute core_pattern). - README: add Jlama and LangChain4j to the Similar Projects section. - Apply clang-format 22.1.5 (train_engine.cpp) and spotless (Java) from the #289 merge. - .gitignore: catch llama/.jqwik-database (un-anchored pattern). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rt1paYztGJ2AKUuBuAGDXE
bernardladenthin
pushed a commit
that referenced
this pull request
Jul 2, 2026
The #289 merge introduced the first Lombok-@Builder-heavy class and it had not cleared the strict quality gates: SpotBugs (13 findings), all fixed while keeping class-level @builder: - 10x MRC_METHOD_RETURNS_CONSTANT on Lombok's synthetic $default$*() methods: @Builder.Default emits these, and they are NOT tagged @lombok.Generated even with lombok.addLombokGeneratedAnnotation=true (a Lombok limitation), so SpotBugs cannot auto-skip them. Suppressed for the $default$* methods only. - IMC_IMMATURE_CLASS_NO_TOSTRING: add @tostring (generated, @lombok.Generated). - IMC_IMMATURE_CLASS_WRONG_FIELD_ORDER: move the static MAPPER above the instance fields. - RCN_REDUNDANT_NULLCHECK in LlamaTrainer: the @NullMarked default treated the native finetuneNative() return as @nonnull; mark it @nullable (JNI can return null on success), making the `error != null` guard meaningful. PIT (was 99% < 100% threshold): - Optimizer.getNativeValue() showed as NO_COVERAGE because its only test lived in TrainingParametersTest (package parameters.*), outside PIT's args.*/value.*/ exception.*/json.* targetTests. Move it to a dedicated args.OptimizerTest so PIT runs it. Score back to 100% (249/249). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Rt1paYztGJ2AKUuBuAGDXE
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Builds a configurable fine-tuning API on top of the training proof of concept (#287): a
TrainingParametersbuilder — serialized as JSON across JNI, the same idiom asModelParameters/InferenceParameters— drivingLlamaTrainer.finetune(TrainingParameters).What it exposes
Every training knob the ggml-opt path supports:
through the JVM heap)
Optimizerenum →ggml_opt_optimizer_type)train_engine.cppparses the JSON config, applies the knobs tocommon_params, and runs the samellama_opt_init/llama_opt_epochloop as upstreamexamples/training/finetune.cpp.Verified
finetuneNativeis exported; the library loads(
NativeLibraryLoadSmokeTest).TrainingParametersbuilder→JSON unit tests pass (3), model-free.LlamaTrainer+TrainingParameters+Optimizercompile through the strict Error Prone / NullAwaypipeline.
LlamaTrainerIntegrationTest, whichself-skips unless
-Dnet.ladenthin.llama.train.model=…is set.Deferred to a focused follow-up
ggml_opt_epoch_callbackhas nouserdata slot, so a Java callback needs a thread-local trampoline; since it can't be exercised
without an actual training run, it is kept out of this change to get proper attention on its own.
llama_opt_param_filterrather than_all) — moreinvolved; upstream's own
finetune.cpptrains all parameters.