You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
refactor: remove LlamaPublisher in favour of consumer-side reactive adapters
Removes the hand-rolled reactive-streams Publisher and the associated
mandatory runtime dependency on org.reactivestreams. Adds consumer-facing
documentation showing how to wrap LlamaIterable with each mainstream
reactive library's resource-management primitive — verified end-to-end
by a new ReactorIntegrationTest using test-scope reactor-core.
Why now
=======
LlamaPublisher (introduced in PR #188 as section 2.3 of the Kotlin SDK
feature comparison) had zero non-test callers. The feature-investigation
document itself describes its source spec as "no longer a roadmap". The
real-world Android consumer LLaMAndroid uses the existing LlamaIterable
API directly inside a Kotlin flow { } block — bypassing the publisher
entirely. Upstream kherud/java-llama.cpp never carried this class.
LlamaIterable already implements Iterable<LlamaOutput> + AutoCloseable —
the contract every reactive library needs to bridge a blocking source:
- Project Reactor : Flux.using(supplier, Flux::fromIterable, ::close)
- RxJava 3 / RxAndroid : Flowable.using(supplier, Flowable::fromIterable, ::close)
- Kotlin Flow : flow { iterable.use { for (x in it) emit(x) } }
- Akka Streams : Source.fromIterator(() -> iterable.iterator())
These are the canonical patterns the libraries themselves recommend for
blocking sources. Keeping a Publisher in the binding forced every
consumer onto the org.reactivestreams runtime dep just to access a class
nobody called.
Critical correctness note: Flux.fromIterable / Flowable.fromIterable do
NOT auto-close AutoCloseable iterables on cancel — the consumer must use
.using(...) or equivalent. The README documents this caveat explicitly;
the ReactorIntegrationTest pins the correct pattern.
Changes
=======
Deletes:
- src/main/java/net/ladenthin/llama/LlamaPublisher.java (175 LOC)
- src/test/java/net/ladenthin/llama/LlamaPublisherTest.java (204 LOC)
- LlamaModel.streamPublisher / streamChatPublisher (23 LOC)
- pom.xml org.reactivestreams runtime dep + version property
- module-info.java javadoc reference
Adds:
- src/test/java/net/ladenthin/llama/ReactorIntegrationTest.java
Mock-iterable contract test (always runs) + real-model gate test
proving end-to-end cancel propagation via Flux.using + LlamaIterable.close
- pom.xml reactor-core + reactor-test at test scope, 3.6.11
- README.md new "Reactive integration" section covering Reactor,
RxJava 3, Kotlin Flow (with LLaMAndroid reference), Akka Streams,
and the why-no-built-in-Publisher rationale
Updates:
- docs/feature-investigation-llama-stack-client-kotlin.md: section 2.3
status now reads "SHIPPED + REVERTED REACTIVE PUBLISHER" with full
rationale and pointer to the README
- TODO.md: new Done entry capturing the decision trail
Net: -331 LOC (-503 source, +172 test/docs); -1 runtime dep
(org.reactivestreams); +2 test-scope deps (reactor-core, reactor-test).
SpotBugs Max+Low: total drops 25 -> 19 (all 6 LlamaPublisher$LlamaSubscription
findings cleared as a side effect: MDM_WAIT_WITHOUT_TIMEOUT x4 +
CWO_CLOSED_WITHOUT_OPENED + PRMC_POSSIBLY_REDUNDANT_METHOD_CALLS).
Tests
=====
ReactorIntegrationTest: 2 tests, mock variant always runs, real-model
variant gated on TestConstants.MODEL_PATH. Mock test proves Reactor
backpressure (request(2) delivers exactly 2 items, never more) and
cleanup-on-cancel (Flux.using cleanup function fires on cancel).
887 of 888 tests pass (the 1 error is the known sandbox-without-native-lib
UnsatisfiedLinkError in RerankingModelTest, unrelated to this change).
-**`javac -Werror` + `-Xlint:all,-serial,-options,-classfile,-processing`** — `3e2efbb`. ~20 EP warnings addressed first (EqualsGetClass on `Pair` via instanceof; MissingOverride on `PoolingType` / `RopeScalingType`; JdkObsolete `LinkedList` → `ArrayList` in `LlamaLoader`; StringSplitter inline-suppressed; 3× StringCaseLocaleUsage `Locale.ROOT` in `OSInfo`; EmptyCatch in `OSInfo.isAlpineLinux`; FutureReturnValueIgnored in `LlamaModel.completeAsync`; Finalize on `LlamaModel.finalize`; MixedMutabilityReturnType in 4 parser methods; EnumOrdinal in `InferenceParameters.setMiroStat`; EscapedEntity in `InferenceParameters` javadoc; 4× TypeParameterUnusedInFormals; AnnotateFormatMethod on `Java8CompatibilityHelper.formatted`; SafeVarargs + varargs on `Java8CompatibilityHelper.listOf`).
0 commit comments