diff --git a/srcmorph/src/main/java/net/ladenthin/srcmorph/provider/LlamaCppJniAiGenerationProvider.java b/srcmorph/src/main/java/net/ladenthin/srcmorph/provider/LlamaCppJniAiGenerationProvider.java index 541c6054..4b8479e4 100644 --- a/srcmorph/src/main/java/net/ladenthin/srcmorph/provider/LlamaCppJniAiGenerationProvider.java +++ b/srcmorph/src/main/java/net/ladenthin/srcmorph/provider/LlamaCppJniAiGenerationProvider.java @@ -175,7 +175,14 @@ private LlamaModel model() { // llama.version alone will NOT surface this: the guard keeps throwing and the knob keeps // looking broken, so the bump checklist has to name it. See TODO.md. if (config.flashAttn()) { - throw new IllegalArgumentException(FLASH_ATTN_UNSUPPORTED_MESSAGE); + // The model path is not decoration: this guard fires on the direct-API path, where + // there is no aiDefinition key to name (the plan-time guard in EngineSupport prefixes + // that one), so without it a caller running several models is told what is wrong but + // not which configuration to change. It also keeps the message out of + // WEM_WEAK_EXCEPTION_MESSAGING, which SpotBugs raises on a throw whose whole message + // is a compile-time constant. + throw new IllegalArgumentException( + "model '" + config.modelPath() + "': " + FLASH_ATTN_UNSUPPORTED_MESSAGE); } // KV-cache quantization. Set independently of each other, but note that a quantized V cache // generally needs Flash Attention above -- llama.cpp refuses the combination otherwise. diff --git a/srcmorph/src/test/java/net/ladenthin/srcmorph/provider/LlamaCppJniKnobSweepTest.java b/srcmorph/src/test/java/net/ladenthin/srcmorph/provider/LlamaCppJniKnobSweepTest.java index d1787ebb..96f34c8e 100644 --- a/srcmorph/src/test/java/net/ladenthin/srcmorph/provider/LlamaCppJniKnobSweepTest.java +++ b/srcmorph/src/test/java/net/ladenthin/srcmorph/provider/LlamaCppJniKnobSweepTest.java @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 package net.ladenthin.srcmorph.provider; +import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; @@ -217,7 +218,12 @@ public void flashAttn_isRefusedRatherThanSilentlyDropped() { try (LlamaCppJniAiGenerationProvider provider = new LlamaCppJniAiGenerationProvider(config, promptSupport)) { final IllegalArgumentException thrown = Assertions.assertThrows(IllegalArgumentException.class, () -> provider.generate(request())); - assertThat(thrown.getMessage(), is(LlamaCppJniAiGenerationProvider.FLASH_ATTN_UNSUPPORTED_MESSAGE)); + // containsString, not equality: the provider prefixes the model it is refusing for, so a + // caller running several models can tell which configuration to change. + assertThat( + thrown.getMessage(), + containsString(LlamaCppJniAiGenerationProvider.FLASH_ATTN_UNSUPPORTED_MESSAGE)); + assertThat(thrown.getMessage(), containsString(NativeLlamaAvailability.modelPath())); } }