forked from kherud/java-llama.cpp
-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathspotbugs-exclude.xml
More file actions
608 lines (561 loc) · 29.4 KB
/
Copy pathspotbugs-exclude.xml
File metadata and controls
608 lines (561 loc) · 29.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
<?xml version="1.0" encoding="UTF-8"?>
<!--
SPDX-FileCopyrightText: 2026 Bernard Ladenthin <bernard.ladenthin@gmail.com>
SPDX-License-Identifier: MIT
-->
<FindBugsFilter
xmlns="https://github.com/spotbugs/filter/3.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubusercontent.com/spotbugs/spotbugs/4.8.6/spotbugs/etc/findbugsfilter.xsd">
<!--
OSInfo is vendored verbatim from xerial/sqlite-jdbc (originally @author leo).
See SPDX-FileCopyrightText header in src/main/java/net/ladenthin/llama/OSInfo.java.
Excluding the class (and its inner classes) from spotbugs analysis since
upstream fixes should land in xerial/sqlite-jdbc rather than be patched here.
-->
<Match>
<Class name="~net\.ladenthin\.llama\.loader\.OSInfo(\$.*)?"/>
</Match>
<!--
ProcessRunner is an internal package-private helper used solely by OSInfo
to invoke platform-detection commands ("uname -o", "uname -m"). It is
already hardened against shell-tokenisation by calling Runtime.exec(String[])
rather than the shell-parsing Runtime.exec(String). findsecbugs still flags
every non-literal Runtime.exec call as COMMAND_INJECTION regardless of which
overload is used; this exclusion documents the assessment that the finding
is theoretical only — there is no public API path that lets an external
caller reach this code, and the two call sites in OSInfo pass hardcoded
literal commands.
-->
<Match>
<Class name="net.ladenthin.llama.loader.ProcessRunner"/>
<Bug pattern="COMMAND_INJECTION"/>
</Match>
<!--
LlamaModel deliberately wraps low-level Jackson IOException into the
project's LlamaException (a RuntimeException subclass) at three JSON-parsing
boundary methods: completeAsJson, getMetricsTyped, getModelMeta. This is the
intended public-API design — IOException is an implementation detail of the
JSON parser, not something callers of a JNI bridge should be forced to handle.
The exception cause is already chained through so the original stack trace
is preserved (see f8c11b0). Spotbugs flags this as
EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS because the resulting RuntimeException
leaves no compile-time hint that an I/O failure happened; we accept that
tradeoff at these three boundary points.
-->
<Match>
<Class name="net.ladenthin.llama.LlamaModel"/>
<Bug pattern="EXS_EXCEPTION_SOFTENING_NO_CONSTRAINTS"/>
<Or>
<Method name="completeAsJson"/>
<Method name="getMetricsTyped"/>
<Method name="getModelMeta"/>
</Or>
</Match>
<!--
ModelParameters intentionally types each enum-valued fluent setter
to its specific enum (CacheType, MiroStat, NumaStrategy,
ReasoningFormat, RopeScalingType, GpuSplitMode) rather than the
shared CliArg interface that those enums all implement. The narrow
type is the API contract:
params.setMirostat(MiroStat.V1) accepted
params.setMirostat(NumaStrategy.DISTRIBUTE) rejected by compiler
If we widened to CliArg as spotbugs OCP suggests, the second call
would silently compile and emit a nonsense CLI value that the
native code would reject at runtime. IDE autocomplete
also relies on the narrow type to surface the right enum
constants. Same design-intent rationale as the STT and EXS
suppressions above.
-->
<Match>
<Class name="net.ladenthin.llama.parameters.ModelParameters"/>
<Bug pattern="OCP_OVERLY_CONCRETE_PARAMETER"/>
<Or>
<Method name="setCacheTypeK"/>
<Method name="setCacheTypeV"/>
<Method name="setMirostat"/>
<Method name="setNuma"/>
<Method name="setReasoningFormat"/>
<Method name="setRopeScaling"/>
<Method name="setSplitMode"/>
</Or>
</Match>
<!--
Same design-intent rationale as the ModelParameters OCP block above:
InferenceParameters.withReasoningFormat(ReasoningFormat) intentionally
types its parameter to the specific ReasoningFormat enum rather than
the shared CliArg interface. The narrow type is the API contract;
widening it would silently accept any CliArg-implementing enum and
emit a nonsense JSON value the native code would reject.
-->
<Match>
<Class name="net.ladenthin.llama.parameters.InferenceParameters"/>
<Bug pattern="OCP_OVERLY_CONCRETE_PARAMETER"/>
<Method name="withReasoningFormat"/>
</Match>
<!--
InferenceParameters and ModelParameters are fluent builders whose
parameters field is a Map<String, String> serving as the CLI / JSON
wire-format passed across JNI to nlohmann/json. Every setter
deliberately serializes its argument (Jackson ArrayNode/ObjectNode
via .toString(), or scalar concat) and stores the string into the
map. Spotbugs STT_TOSTRING_STORED_IN_FIELD flags every site because
a String pinned by toString() cannot be reformatted with a different
locale/encoding later — that pin is the whole point of the
serialization step. Same design rationale as the EXS suppression on
LlamaModel above.
-->
<Match>
<Or>
<Class name="net.ladenthin.llama.parameters.InferenceParameters"/>
<Class name="net.ladenthin.llama.parameters.ModelParameters"/>
</Or>
<Bug pattern="STT_TOSTRING_STORED_IN_FIELD"/>
</Match>
<!--
PATH_TRAVERSAL_IN (reviewed 2026-06-26): confirmed a false positive for
this JNI library, so this suppression is permanent and no code fix is
appropriate. Two operator-controlled path sites are flagged:
1. LlamaLoader (native-library bootstrap) resolves
libjllama.{so,dylib,dll} from three JVM-launch inputs:
a. the net.ladenthin.llama.lib.path system property (a directory)
b. java.library.path entries
c. java.io.tmpdir plus a hard-coded basename
2. OfflineModelGuard.check() does Files.exists(Paths.get(model)),
where model is ModelParameters.getModel() (the configured local
model-file path), to fail fast when the offline flag is set and the
local model is absent. This is a read-only existence check; the
path is not opened or written here.
findsecbugs taints every non-literal Paths.get argument (System
properties and CLI/builder config count as taint sources), but all of
these inputs are the operator's own process configuration set at launch,
not untrusted end-user input crossing a privilege boundary. An attacker
who can set the model path, the lib.path property, or java.library.path
already controls the JVM; there is no traversal boundary to cross.
Why no fix: the canonicalize-and-restrict-to-an-allowed-root remediation
does not apply. Pointing at an arbitrary GGUF or library directory
anywhere on disk is the entire purpose of these settings, so there is no
meaningful allowed root, and a parent-directory-rejecting check would
break the legitimate relative-path case. An embedder that exposes the
model path to untrusted remote users must validate it before calling this
library; that lies outside the library's API contract.
-->
<Match>
<Or>
<Class name="net.ladenthin.llama.loader.LlamaLoader"/>
<Class name="net.ladenthin.llama.loader.OfflineModelGuard"/>
<Class name="net.ladenthin.llama.parameters.ModelParameters"/>
</Or>
<Bug pattern="PATH_TRAVERSAL_IN"/>
</Match>
<!--
LlamaIterator and LlamaModel form a deliberate producer/consumer
cycle: LlamaModel.generate(...) returns a LlamaIterable that
yields LlamaIterator instances; each LlamaIterator calls back
into LlamaModel to fetch the next token via the native bridge.
This is the standard shape of a streaming iterator that drives
a backend (java.util.Iterator semantics require it). The static
class cycle is a side effect of the Iterator/Iterable API
contract, not a design defect.
Breaking the static cycle with an interface (e.g. a TokenSource
that LlamaModel implements and LlamaIterator depends on) would
add a fake abstraction with one implementer and no decoupling
value; the runtime coupling is identical either way.
-->
<Match>
<Or>
<Class name="net.ladenthin.llama.LlamaIterator"/>
<Class name="net.ladenthin.llama.LlamaModel"/>
</Or>
<Bug pattern="FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY"/>
</Match>
<!--
Session is a thin non-owning wrapper around a LlamaModel: one LlamaModel
wraps a native llama.cpp context that is intentionally shared across
multiple Session instances (one per slot id). The model owns native
memory and must NOT be defensively copied — Session deliberately holds
the same reference the caller passed in, and Session.close() calls
model.eraseSlot(slotId), never model.close(). This is the documented
dependency-injection contract; spotbugs flags it as EI_EXPOSE_REP2
because the constructor stores an externally-mutable object, which is
true but by design.
-->
<Match>
<Class name="net.ladenthin.llama.Session"/>
<Bug pattern="EI_EXPOSE_REP2"/>
<Method name="<init>"/>
</Match>
<!--
USBR_UNNECESSARY_STORE_BEFORE_RETURN on Lombok-generated equals / hashCode /
canEqual / toString.
Lombok's @EqualsAndHashCode and @ToString annotation processors inject the
textbook polynomial-hash bytecode pattern (and lombok.config already emits
@lombok.Generated on every synthetic member via
lombok.addLombokGeneratedAnnotation = true):
int result = 1;
result = result * 59 + ($field == null ? 43 : $field.hashCode());
...
return result; // USBR fires here, on the istore_N / iload_N / ireturn triplet
SpotBugs core honours @lombok.Generated and skips its own detectors on those
members, but the fb-contrib plugin's USBR detector does NOT — fb-contrib
is a separate plugin family with its own filter pipeline. Suppressing USBR on
equals / hashCode / canEqual / toString matches every method name Lombok can
emit. The collateral cost is small: any handwritten member of those four names
that genuinely stores-then-immediately-returns is either a debugger-friendly
local-variable pattern or a micro-optimisation, both intentional here.
Cross-repo invariant — see `../workspace/policies/lombok-config.md`.
-->
<Match>
<Or>
<Method name="equals"/>
<Method name="hashCode"/>
<Method name="canEqual"/>
<Method name="toString"/>
</Or>
<Bug pattern="USBR_UNNECESSARY_STORE_BEFORE_RETURN"/>
</Match>
<!--
fb-contrib OPM_OVERLY_PERMISSIVE_METHOD ("Method is declared more
permissively than is used in the code base") suppressed PROJECT-WIDE.
Rationale (kept aligned cross-repo with BitcoinAddressFinder):
- Current package layout groups most production Java in
net.ladenthin.llama + a thin sibling package set. Any method
called only from same-package callers is flagged as "could be
package-private". Those answers are correct today but unstable:
once the planned package-architecture refactor splits the root
package into proper layers, methods that today are correctly
package-private will need to become public to cross the new
boundaries. Tightening now produces mechanical churn highly
likely to be reverted by the refactor.
- Cross-repo decision + per-category breakdown recorded in
workspace/crossrepostatus.md ("OPM scope-tightening — after
package refactor"). The same rule is suppressed in BAF.
TODO: re-enable this rule (delete this Match block) once the
package refactor has settled — at that point genuine "method
exposed beyond its actual call site" findings become stable
signals worth fixing.
-->
<Match>
<Bug pattern="OPM_OVERLY_PERMISSIVE_METHOD"/>
</Match>
<!--
ChatRequest is an immutable value class. Its messages/tools fields
are stored as Collections.unmodifiableList(...) views, so the
getters CANNOT actually leak the internal representation: any
attempt to mutate the returned list throws UnsupportedOperationException
(covered by ChatRequestTest.messagesAccessorIsUnmodifiable /
toolsAccessorIsUnmodifiable). SpotBugs flags every "return this.field"
from a non-array reference field as EI_EXPOSE_REP without tracking
whether the field was unmodifiable-wrapped at construction time;
the wrapping is verified by tests, so the finding is a false positive.
-->
<Match>
<Class name="net.ladenthin.llama.parameters.ChatRequest"/>
<Bug pattern="EI_EXPOSE_REP"/>
<Or>
<Method name="getMessages"/>
<Method name="getTools"/>
</Or>
</Match>
<!--
LlamaModel.ctx is the per-instance native handle: a long pointer
into the llama.cpp context owned by THIS LlamaModel instance.
fb-contrib's SPP_FIELD_COULD_BE_STATIC detector observes that
the field is only assigned inside loadModel (called from the
constructor) and never reassigned, and concludes the field could
be promoted to static. That is incorrect: every LlamaModel wraps
its OWN native context, and making ctx static would cause every
instance to share one handle — corrupting state across parallel
inference calls and double-freeing on close().
-->
<Match>
<Class name="net.ladenthin.llama.LlamaModel"/>
<Bug pattern="SPP_FIELD_COULD_BE_STATIC"/>
<Field name="ctx"/>
</Match>
<!--
CancellationToken and ChatTranscript are lifecycle handles managed by
identity, not value: a CancellationToken owns a mutable cancellation
flag observed across threads, and ChatTranscript is an append-only
transcript owned by a single Session and never compared by value.
Both classes deliberately do NOT generate Lombok @EqualsAndHashCode
(documented in their Javadocs) — fb-contrib's IMC_NO_EQUALS check
is therefore a false positive for both.
-->
<Match>
<Or>
<Class name="net.ladenthin.llama.callback.CancellationToken"/>
<Class name="net.ladenthin.llama.value.ChatTranscript"/>
</Or>
<Bug pattern="IMC_IMMATURE_CLASS_NO_EQUALS"/>
</Match>
<!--
TimingsLogger emits its events under the documented public logger name
"net.ladenthin.llama.timings" (see CLAUDE.md > System Properties Reference
and the README), NOT the FQN of the TimingsLogger class. That separation
lets operators raise/lower the per-run-timing line independently of
application logs. fb-contrib's LO_SUSPECT_LOG_CLASS detector flags any
logger whose name does not match the enclosing class FQN; here the
mismatch is the public contract.
-->
<Match>
<Class name="net.ladenthin.llama.json.TimingsLogger"/>
<Bug pattern="LO_SUSPECT_LOG_CLASS"/>
</Match>
<!--
Java8CompatibilityHelper.formatted is a thin wrapper around
String.format that intentionally accepts runtime-supplied format
strings — the helper exists precisely so that Java 11+'s
String#formatted() can be used uniformly on the Java 8 baseline.
fb-contrib's FORMAT_STRING_MANIPULATION fires on any non-literal
format argument; the wrapper is the documented escape hatch.
-->
<Match>
<Class name="net.ladenthin.llama.loader.Java8CompatibilityHelper"/>
<Bug pattern="FORMAT_STRING_MANIPULATION"/>
<Method name="formatted"/>
</Match>
<!--
ToolHandler.invoke is the functional-interface contract for caller-
supplied tool handlers. `throws Exception` is the right shape because
the handler body is user code that can throw anything; LlamaModel's
chatWithTools agent loop catches the broad Exception and reports it
back to the model as a {"error":"..."} tool result rather than
aborting the request. Narrowing the throws clause would force every
handler implementation to wrap arbitrary checked exceptions for
no behavioural benefit.
-->
<Match>
<Class name="net.ladenthin.llama.callback.ToolHandler"/>
<Bug pattern="THROWS_METHOD_THROWS_CLAUSE_BASIC_EXCEPTION"/>
<Method name="invoke"/>
</Match>
<!--
LlamaModel.completeBatch / completeBatchWithStats / chatBatch dispatch N async requests, then
deliberately join EVERY future before re-throwing the first captured failure (a
CompletionException) — so a partial failure never abandons sibling requests running unobserved.
Re-throwing the captured RuntimeException is the intended behaviour; fb-contrib flags any
`throw <RuntimeException>` as THROWS_METHOD_THROWS_RUNTIMEEXCEPTION.
-->
<Match>
<Class name="net.ladenthin.llama.LlamaModel"/>
<Bug pattern="THROWS_METHOD_THROWS_RUNTIMEEXCEPTION"/>
<Or>
<Method name="completeBatch"/>
<Method name="completeBatchWithStats"/>
<Method name="chatBatch"/>
</Or>
</Match>
<!--
ChatMessage's public constructor intentionally accepts List<ToolCall> (the natural,
ergonomic public-API type, matching the parts constructor). fb-contrib notes the parameter
is only consumed as a Collection (defensively copied into an ArrayList) and suggests widening
it; doing so on a public constructor would be a binary-incompatible API change for no caller
benefit, so the concrete List type is kept deliberately.
-->
<Match>
<Class name="net.ladenthin.llama.value.ChatMessage"/>
<Bug pattern="OCP_OVERLY_CONCRETE_COLLECTION_PARAMETER"/>
<Method name="<init>"/>
</Match>
<!--
ChatMessage.requireNonNull is a precondition guard whose only
meaningful state to report is the parameter name itself (the value
is null by definition at the throw point). fb-contrib's WEM detector
recognises the static-string IllegalArgumentException as "weak", but
there is no additional state-dependent context to add at this guard.
-->
<Match>
<Class name="net.ladenthin.llama.value.ChatMessage"/>
<Bug pattern="WEM_WEAK_EXCEPTION_MESSAGING"/>
<Method name="requireNonNull"/>
</Match>
<!--
The OpenAI-compatible server (net.ladenthin.llama.server.*) is a CLI entry point:
the model path, host, port and alias all come from command-line arguments supplied
by whoever launches the process. findsecbugs flags Paths.get on the model path
(PATH_TRAVERSAL_IN) and the startup log lines that echo these values
(CRLF_INJECTION_LOGS) because they are non-literal, but the threat model is identical
to the LlamaLoader PATH_TRAVERSAL suppression above: an attacker who can set the
server's command line has already won, and there is no untrusted end-user input
reaching these paths or log statements. There is also no meaningful "allowed root"
to canonicalise the operator-chosen model path against.
-->
<Match>
<Class name="~net\.ladenthin\.llama\.server\..*"/>
<Or>
<Bug pattern="PATH_TRAVERSAL_IN"/>
<Bug pattern="CRLF_INJECTION_LOGS"/>
</Or>
</Match>
<!--
OpenAiServerCli.parse is a flat command-line flag dispatcher: a single switch over
the known flags, one case per option, read top to bottom. javac desugars a String
switch into a hashCode lookup plus an equals chain (two branches per case), which
fb-contrib's bytecode-level CC_CYCLOMATIC_COMPLEXITY counts as a very high score.
The source complexity is low and table-flat; extracting the cases into a dispatch
map would not make it clearer, so we accept the detector artifact here.
-->
<Match>
<Class name="net.ladenthin.llama.server.OpenAiServerCli"/>
<Bug pattern="CC_CYCLOMATIC_COMPLEXITY"/>
<Method name="parse"/>
</Match>
<!--
findsecbugs IMPROPER_UNICODE on the OpenAI-compatible server (net.ladenthin.llama.server.*).
Every finding is a case-insensitive comparison of an HTTP method token against an ASCII literal
(e.g. "GET".equalsIgnoreCase(exchange.getRequestMethod()), the CORS "OPTIONS" preflight check).
HTTP method names are ASCII by specification (RFC 7230 / 7231), so there is no Unicode
case-folding collision to exploit here; the detector fires on any equalsIgnoreCase regardless of
the operand domain. Same server findsecbugs false-positive category as the PATH_TRAVERSAL_IN /
CRLF_INJECTION_LOGS block above.
-->
<Match>
<Class name="~net\.ladenthin\.llama\.server\..*"/>
<Bug pattern="IMPROPER_UNICODE"/>
</Match>
<!--
ContentPart.inputAudio validates the audio-format token the canonical way: lowercase with the
locale-safe Locale.ROOT, then compare against the two allowed ASCII literals ("wav" / "mp3"),
throwing IllegalArgumentException otherwise. findsecbugs fires IMPROPER_UNICODE on the
case-transform and LSC_LITERAL_STRING_COMPARISON on the literal .equals, but the operand domain
is a fixed pair of ASCII format names — no Unicode case-folding collision exists, and comparing
the normalised token against the literals is exactly the intended validation. Same false-positive
category as the server.* IMPROPER_UNICODE block above. (Pre-existing finding from the merged
audio-input feature; suppressed here so the branch's analysis is clean.)
-->
<Match>
<Class name="net.ladenthin.llama.value.ContentPart"/>
<Method name="inputAudio"/>
<Or>
<Bug pattern="IMPROPER_UNICODE"/>
<Bug pattern="LSC_LITERAL_STRING_COMPARISON"/>
</Or>
</Match>
<!--
LlamaModelBackend.stream bridges the model's Consumer-based streaming API (which cannot throw a
checked exception) back to the IOException-throwing ChunkSink. A sink IOException is relayed out
of the consumer as an UncheckedIOException, caught here, and the ORIGINAL IOException is rethrown
(it was stashed before wrapping). fb-contrib LEST flags the catch block because the thrown object
differs from the caught one, but the rethrown IOException is exactly the underlying cause with
its stack trace intact, so no history is lost. This is the standard UncheckedIOException unwrap
idiom.
-->
<Match>
<Class name="net.ladenthin.llama.server.LlamaModelBackend"/>
<Bug pattern="LEST_LOST_EXCEPTION_STACK_TRACE"/>
<Method name="stream"/>
</Match>
<!--
Input-validation precondition guards on the server's request parsers throw
IllegalArgumentException with a fixed, descriptive message naming the offending field
("'query' must be a string", "'messages' must be a non-empty array", ...). There is no
additional state-dependent context worth adding at these guards: the value is malformed or
absent by definition at the throw point. Same rationale as the ChatMessage.requireNonNull WEM
suppression above.
-->
<Match>
<Class name="net.ladenthin.llama.server.OaiRerankSupport"/>
<Bug pattern="WEM_WEAK_EXCEPTION_MESSAGING"/>
<Or>
<Method name="readQuery"/>
<Method name="readDocuments"/>
</Or>
</Match>
<Match>
<Class name="net.ladenthin.llama.server.OpenAiRequestMapper"/>
<Bug pattern="WEM_WEAK_EXCEPTION_MESSAGING"/>
<Or>
<Method name="toInferenceParameters"/>
<Method name="toCompletionParameters"/>
</Or>
</Match>
<!--
OpenAiCompatServer.main blocks the launcher thread forever (Thread.join with no timeout) so the
JVM stays alive while the daemon HTTP worker threads serve requests; a shutdown hook performs the
orderly close. A bounded join would defeat the purpose (the process must run until killed), so
MDM_WAIT_WITHOUT_TIMEOUT is by design at this single entry point.
-->
<Match>
<Class name="net.ladenthin.llama.server.OpenAiCompatServer"/>
<Bug pattern="MDM_WAIT_WITHOUT_TIMEOUT"/>
<Method name="main"/>
</Match>
<!--
OpenAiSseFormatter.sseDone() and heartbeat() are self-documenting accessors for the two fixed SSE
protocol tokens (the stream terminator and the comment-line keep-alive). They sit alongside
sseData(String) to form one cohesive SSE-framing vocabulary; the constant return value IS the
wire-format contract (with the Javadoc explaining each). fb-contrib MRC suggests folding a
constant-returning method into a field, but keeping the parallel method API is the deliberate
choice here.
-->
<Match>
<Class name="net.ladenthin.llama.server.OpenAiSseFormatter"/>
<Bug pattern="MRC_METHOD_RETURNS_CONSTANT"/>
<Or>
<Method name="sseDone"/>
<Method name="heartbeat"/>
</Or>
</Match>
<!--
ResponsesStreamTranslator.end() builds the closing Responses SSE events. fb-contrib PRMC flags
the repeated calls to ResponsesApiSupport.dataObject() as "possibly redundant", but that factory
returns a FRESH ObjectNode on each call by design (each SSE event needs its own independent data
envelope); caching and reusing one node would corrupt the event stream by sharing mutable JSON
across events. The repeated calls are therefore intentional, not redundant.
-->
<Match>
<Class name="net.ladenthin.llama.server.ResponsesStreamTranslator"/>
<Bug pattern="PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS"/>
<Method name="end"/>
</Match>
<!--
ToolCallDeltaAccumulator is a transient, mutable state machine: it merges the incremental
delta.tool_calls fragments of ONE streaming chat completion by index, and is discarded when that
stream ends. It is managed by identity (one instance per in-flight stream) and is never compared
by value or used as a Map/Set key, so equals/hashCode would be meaningless here (its inner Partial
holder has no value semantics either). It carries a Lombok @ToString purely for debug output;
that toString satisfied IMC_IMMATURE_CLASS_NO_TOSTRING and advanced the fb-contrib IMC detector to
IMC_IMMATURE_CLASS_NO_EQUALS, which is suppressed here for the same identity-managed rationale as
the CancellationToken / ChatTranscript block above.
-->
<Match>
<Class name="net.ladenthin.llama.server.ToolCallDeltaAccumulator"/>
<Bug pattern="IMC_IMMATURE_CLASS_NO_EQUALS"/>
</Match>
<!--
LlamaModelBackend and OpenAiCompatServer are protocol-infrastructure / service classes whose
instance fields are opaque runtime resources with no meaningful toString: a native-backed
LlamaModel handle plus a stateless request mapper (LlamaModelBackend); the JDK HttpServer, an
anonymous CORS Filter, and two executor services (OpenAiCompatServer). A generated toString would
only emit identity hashes for those fields, which is exactly what CodeQL's java/default-tostring
query flags. A toString therefore adds no debugging value on these two classes and is
intentionally omitted, so IMC_IMMATURE_CLASS_NO_TOSTRING is suppressed here. (The
translator/accumulator classes keep their Lombok toString, whose fields render meaningful state.)
-->
<Match>
<Or>
<Class name="net.ladenthin.llama.server.LlamaModelBackend"/>
<Class name="net.ladenthin.llama.server.OpenAiCompatServer"/>
</Or>
<Bug pattern="IMC_IMMATURE_CLASS_NO_TOSTRING"/>
</Match>
<!--
TextToSpeech is a native-handle wrapper like LlamaModel: its only instance field is the opaque
`long handle` to the native OuteTTS engine. A generated toString would emit just that pointer
value (no meaningful state), exactly the IMC_IMMATURE_CLASS_NO_TOSTRING / java/default-tostring
case suppressed for LlamaModelBackend above, so toString is intentionally omitted. And
synthesize() guards the closed handle with a fixed, descriptive precondition message
("TextToSpeech is closed") — the value is closed-or-not by definition at that throw, with no
extra state worth adding (same rationale as the server request-parser WEM suppressions above).
-->
<Match>
<Class name="net.ladenthin.llama.TextToSpeech"/>
<Bug pattern="IMC_IMMATURE_CLASS_NO_TOSTRING"/>
</Match>
<Match>
<Class name="net.ladenthin.llama.TextToSpeech"/>
<Bug pattern="WEM_WEAK_EXCEPTION_MESSAGING"/>
<Method name="synthesize"/>
</Match>
</FindBugsFilter>