|
This version is still in development and is not considered stable yet. For the latest stable version, please use Spring AI 2.0.1! |
Qxotic jinfer Embeddings
jinfer is a pure-JVM inference engine from the Qxotic AI project. It runs GGUF embedding and reranking models inside your application’s JVM, without a separate inference server or external service.
Spring AI applications use jinfer through two classes:
-
JinferEmbeddingModelimplements Spring AI’sEmbeddingModel. -
JinferDocumentPostProcessorimplements the RAGDocumentPostProcessorand reranks retrieved documents with a local reranker model. See Reranking.
The jinfer Spring AI integration is an external module maintained by the Qxotic AI project, not part of the Spring AI repository.
Its artifacts are published to Maven Central under the com.qxotic group.
|
Prerequisites
The jinfer embedding model has the same requirements as the jinfer chat model:
-
Java 25 or later.
-
The JVM flags
--add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED. -
Spring Boot 4.1 and Spring AI 2.0.
Models are configured as a local .gguf path or as a model reference, such as Qwen/Qwen3-Embedding-0.6B-GGUF:Q8_0.
A model reference is downloaded on first use and cached.
Supported Models
Model |
Artifact |
Notes |
Qwen3-Embedding (0.6B, 4B, 8B) |
|
Matryoshka-trained: the output dimension can be reduced per request. |
|
Fixed output dimension. |
|
|
Reranker. Scores are probabilities in |
|
|
Reranker. Scores are unbounded MaxSim sums. |
jinfer-models-all includes all model families.
Auto-configuration
Add the jinfer Spring Boot starter and the model families you need. Import both BOMs as shown in the jinfer chat model documentation.
-
Maven
-
Gradle
<dependency>
<groupId>com.qxotic</groupId>
<artifactId>jinfer-spring-ai-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.qxotic</groupId>
<artifactId>jinfer-qwen3</artifactId>
</dependency>
dependencies {
implementation platform('org.springframework.ai:spring-ai-bom:2.0.1')
implementation platform('com.qxotic:jinfer-bom:0.3.1')
implementation 'com.qxotic:jinfer-spring-ai-spring-boot-starter'
implementation 'com.qxotic:jinfer-qwen3'
}
The optional jam-native and jam-vector runtime dependencies speed up inference.
See Optional Matrix-Multiplication Backends.
Embedding Properties
|
Unlike the chat model, the jinfer embedding model must be selected explicitly with |
The prefix spring.ai.jinfer.embedding is the property prefix for the jinfer embedding model.
Property |
Description |
Default |
spring.ai.model.embedding |
Selects the embedding provider. Set it to |
- |
spring.ai.jinfer.embedding.model |
Required. A local GGUF path or a model reference, for example |
- |
spring.ai.jinfer.embedding.context-capacity |
The batching window, and the maximum length of each embedded text, in tokens. A larger value fits more texts into one forward pass and accepts longer texts, but uses more memory. |
|
spring:
ai:
model:
embedding: jinfer
jinfer:
embedding:
model: Qwen/Qwen3-Embedding-0.6B-GGUF:Q8_0
context-capacity: 2048
The chat and embedding models can be configured side by side.
For example, a RAG application can run its chat model, embedding model and an in-memory SimpleVectorStore in a single JVM.
Runtime Options
JinferEmbeddingModel accepts the standard Spring AI EmbeddingOptions:
-
dimensionstruncates the vectors to a smaller size and L2-normalizes them again. This works only for Matryoshka-trained models: Qwen3-Embedding accepts any size from 32 up to its native dimension. Models with a fixed output dimension reject this option instead of returning a truncated prefix. -
modelmust be the loaded model, or unset. Each instance is bound to one GGUF file.
EmbeddingResponse response = embeddingModel.call(
new EmbeddingRequest(List.of("Hello World", "World is big and salvation is near"),
EmbeddingOptions.builder().dimensions(256).build()));
JinferEmbeddingModel processes inputs in batches: one forward pass embeds many texts at once.
Ingesting hundreds of RAG chunks therefore takes a handful of passes, not one pass per chunk.
The token counts in the returned usage come from the model’s real tokenizer, not an estimate.
JinferEmbeddingModel records Spring AI’s standard embedding model observations, using the jinfer provider name.
Reranking
A reranker scores each retrieved document by whether it actually answers the query, rather than by embedding similarity.
JinferDocumentPostProcessor implements the RAG DocumentPostProcessor interface and runs a local reranker model.
Reranking Properties
The reranker bean is created when spring.ai.jinfer.rerank.model is set.
Property |
Description |
Default |
spring.ai.jinfer.rerank.model |
A local GGUF path or a model reference for a reranker, for example |
- |
spring.ai.jinfer.rerank.context-capacity |
The maximum length of the query and document together, in tokens. |
|
spring.ai.jinfer.rerank.instruction |
The task instruction given to the reranker. When unset, the wording from the model card is used. |
- |
spring.ai.jinfer.rerank.top-k |
Keeps only the |
|
spring.ai.jinfer.rerank.min-score |
Drops documents that score below this value. Qwen3-Reranker scores are probabilities, where |
- |
Using the Reranker in a RAG Pipeline
Add the auto-configured DocumentPostProcessor to a RetrievalAugmentationAdvisor:
@Bean
RetrievalAugmentationAdvisor ragAdvisor(VectorStore vectorStore, DocumentPostProcessor reranker) {
return RetrievalAugmentationAdvisor.builder()
.documentRetriever(VectorStoreDocumentRetriever.builder()
.vectorStore(vectorStore)
.topK(20) // retrieve generously ...
.build())
.documentPostProcessors(reranker) // ... then let the reranker keep the best ones
.build();
}
The returned documents are sorted best first.
Each document’s getScore() holds the reranker’s relevance score, which replaces the similarity score from the vector store.
Documents with equal scores keep their original order.
All documents in one call share the processed query prefix, so the query is processed once rather than once per document.
Manual Configuration
To use jinfer without Spring Boot auto-configuration, depend on com.qxotic:jinfer-spring-ai and the model families you need.
For reranking, also add org.springframework.ai:spring-ai-rag.
try (var embeddingModel = JinferEmbeddingModel.builder()
.model("Qwen/Qwen3-Embedding-0.6B-GGUF:Q8_0") // or .modelPath(Path.of("embed.gguf"))
.contextCapacity(2048)
.build();
var reranker = JinferDocumentPostProcessor.builder()
.model("mradermacher/Qwen3-Reranker-0.6B-GGUF:Q8_0")
.topK(1)
.build()) {
float[] vector = embeddingModel.embed("How do I reset my password?");
System.out.println("Dimensions: " + vector.length);
List<Document> ranked = reranker.process(new Query("How do I reset my password?"), List.of(
new Document("Use the password reset link on the sign-in page."),
new Document("Standard shipping takes three business days.")));
}
When you build the models yourself, your application owns them: use try-with-resources or close them at shutdown.
Each instance processes requests one at a time.
For parallel processing, build several instances: the operating system’s page cache shares the memory-mapped weights between them.
Alternatively, load the weights into an arena that your code manages, build with model(loaded), and create additional pipelines with fork().