diff --git a/README.md b/README.md
index 380fc51d..1c85b7f4 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,7 @@ Find the most up-to-date information on the [GPT4All Website](https://gpt4all.io
* :computer: Official Typescript Bindings
* :computer: Official GoLang Bindings
* :computer: Official C# Bindings
+* :computer: Official Java Bindings
## Contributing
diff --git a/gpt4all-bindings/java/.gitignore b/gpt4all-bindings/java/.gitignore
new file mode 100644
index 00000000..8c3a43d3
--- /dev/null
+++ b/gpt4all-bindings/java/.gitignore
@@ -0,0 +1,2 @@
+# Make sure native directory never gets commited to git for the project.
+/src/main/resources/native
\ No newline at end of file
diff --git a/gpt4all-bindings/java/README.md b/gpt4all-bindings/java/README.md
new file mode 100644
index 00000000..fb9f523d
--- /dev/null
+++ b/gpt4all-bindings/java/README.md
@@ -0,0 +1,105 @@
+# Java bindings
+
+Java bindings let you load a gpt4all library into your Java application and execute text
+generation using an intuitive and easy to use API. No GPU is required because gpt4all system executes on the cpu.
+The gpt4all models are quantized to easily fit into system RAM and use about 4 to 7GB of system RAM.
+
+## Getting Started
+You can add Java bindings into your Java project by adding dependency to your project:
+
+**Maven**
+```
+
+ com.hexadevlabs
+ gpt4all-java-binding
+ 1.1.2
+
+```
+**Gradle**
+```
+implementation 'com.hexadevlabs:gpt4all-java-binding:1.1.2'
+```
+
+To add the library dependency for another build system see [Maven Central Java bindings](https://central.sonatype.com/artifact/com.hexadevlabs/gpt4all-java-binding/).
+
+To download a model binary weights file use an url such as https://gpt4all.io/models/ggml-gpt4all-j-v1.3-groovy.bin.
+
+For information about other models available see [Model file list](https://github.com/nomic-ai/gpt4all/tree/main/gpt4all-chat#manual-download-of-models).
+
+### Sample code
+```java
+public class Example {
+ public static void main(String[] args) {
+
+ String prompt = "### Human:\nWhat is the meaning of life\n### Assistant:";
+
+ // Replace the hardcoded path with the actual path where your model file resides
+ String modelFilePath = "C:\\Users\\felix\\AppData\\Local\\nomic.ai\\GPT4All\\ggml-gpt4all-j-v1.3-groovy.bin";
+
+ try (LLModel model = new LLModel(Path.of(modelFilePath))) {
+
+ // May generate up to 4096 tokens but generally stops early
+ LLModel.GenerationConfig config = LLModel.config()
+ .withNPredict(4096).build();
+
+ // Will also stream to Standard out
+ String fullGeneration = model.generate(prompt, config, true);
+
+ } catch (Exception e) {
+ // Exception generally may happen if model file fails to load
+ // for a number of reasons such as file not found.
+ // It is possible that Java may not be able to dynamically load the native shared library or
+ // the llmodel shared library may not be able to dynamically load the backend
+ // implementation for the model file you provided.
+ //
+ // Once the LLModel class is successfully loaded into memory the text generation calls
+ // generally should not throw exceptions.
+ e.printStackTrace(); // Printing here but in production system you may want to take some action.
+ }
+ }
+
+}
+```
+
+For a maven based sample project that uses this library see [Sample project](https://github.com/felix-zaslavskiy/gpt4all-java-bindings-sample)
+
+### Additional considerations
+#### Logger warnings
+The Java bindings library may produce a warning:
+```
+SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
+SLF4J: Defaulting to no-operation (NOP) logger implementation
+SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
+```
+If you don't have a SLF4J binding included in your project. Java bindings only use logging for informational
+purposes, so logger is not essential to correctly use the library. You can ignore this warning if you don't have SLF4J bindings
+in your project.
+
+To add a simple logger using maven dependency you may use:
+```
+
+ org.slf4j
+ slf4j-simple
+ 1.7.36
+
+```
+
+#### Loading your native libraries
+1. Java bindings package jar comes bundled with native library files for Windows, macOS and Linux. These library files are
+copied to a temporary directory and loaded at runtime. For advanced users who may want to package shared libraries into Docker containers
+or want to use a custom build of the shared libraries and ignore the once bundled with the java package they have option
+to load libraries from your local directory by setting a static property to the location of library files.
+There are no guarantees of compatibility if used in such a way so be careful if you really want to do it.
+
+For example:
+```java
+class Example {
+ public static void main(String[] args) {
+ // gpt4all native shared libraries location
+ LLModel.LIBRARY_SEARCH_PATH = "C:\\Users\\felix\\gpt4all\\lib\\";
+ // ... use the library normally
+ }
+}
+```
+2. Not every avx only shared library is bundled with the jar right now to reduce size. Only the libgptj-avx is included.
+If you are running into issues please let us know using the gpt4all project issue tracker https://github.com/nomic-ai/gpt4all/issues.
diff --git a/gpt4all-bindings/java/pom.xml b/gpt4all-bindings/java/pom.xml
new file mode 100644
index 00000000..647b6876
--- /dev/null
+++ b/gpt4all-bindings/java/pom.xml
@@ -0,0 +1,202 @@
+
+
+ 4.0.0
+
+ com.hexadevlabs
+ gpt4all-java-binding
+ 1.1.2
+ jar
+
+
+ 11
+ 11
+ UTF-8
+
+
+ ${project.groupId}:${project.artifactId}
+ Java bindings for GPT4ALL LLM
+ https://github.com/nomic-ai/gpt4all
+
+
+ The Apache License, Version 2.0
+ https://github.com/nomic-ai/gpt4all/blob/main/LICENSE.txt
+
+
+
+
+ Felix Zaslavskiy
+ felixz@hexadevlabs.com
+ https://github.com/felix-zaslavskiy/
+
+
+
+ scm:git:git://github.com/nomic-ai/gpt4all.git
+ scm:git:ssh://github.com/nomic-ai/gpt4all.git
+ https://github.com/nomic-ai/gpt4all/tree/main
+
+
+
+
+ com.github.jnr
+ jnr-ffi
+ 2.2.13
+
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.36
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ 5.9.2
+ test
+
+
+
+
+
+ ossrh
+ https://s01.oss.sonatype.org/content/repositories/snapshots
+
+
+ ossrh
+ https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/
+
+
+
+
+
+
+ src/main/resources
+
+
+ ${project.build.directory}/generated-resources
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 3.0.0
+
+ 0
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.3.1
+
+
+ copy-resources
+
+ validate
+
+ copy-resources
+
+
+ ${project.build.directory}/generated-resources
+
+
+ C:\Users\felix\dev\gpt4all_java_bins\release_1_1_1_Jun8_2023
+
+
+
+
+
+
+
+
+
+ org.sonatype.plugins
+ nexus-staging-maven-plugin
+ 1.6.13
+ true
+
+ ossrh
+ https://s01.oss.sonatype.org/
+ true
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+ 2.2.1
+
+
+ attach-sources
+
+ jar-no-fork
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-javadoc-plugin
+ 3.5.0
+
+
+ attach-javadocs
+
+ jar
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-gpg-plugin
+ 1.5
+
+
+ sign-artifacts
+ verify
+
+ sign
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.6.0
+
+
+ jar-with-dependencies
+
+
+
+ com.hexadevlabs.gpt4allsample.Example4
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java
new file mode 100644
index 00000000..d571b8d2
--- /dev/null
+++ b/gpt4all-bindings/java/src/main/java/com/hexadevlabs/gpt4all/LLModel.java
@@ -0,0 +1,349 @@
+package com.hexadevlabs.gpt4all;
+
+import jnr.ffi.Pointer;
+
+import java.io.ByteArrayOutputStream;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class LLModel implements AutoCloseable {
+
+ /**
+ * Config used for how to decode LLM outputs.
+ * High temperature closer to 1 gives more creative outputs
+ * while low temperature closer to 0 produce more precise outputs.
+ *
+ * Use builder to set settings you want.
+ */
+ public static class GenerationConfig extends LLModelLibrary.LLModelPromptContext {
+
+ private GenerationConfig() {
+ super(jnr.ffi.Runtime.getSystemRuntime());
+ logits_size.set(0);
+ tokens_size.set(0);
+ n_past.set(0);
+ n_ctx.set(1024);
+ n_predict.set(128);
+ top_k.set(40);
+ top_p.set(0.95);
+ temp.set(0.28);
+ n_batch.set(8);
+ repeat_penalty.set(1.1);
+ repeat_last_n.set(10);
+ context_erase.set(0.55);
+ }
+
+ public static class Builder {
+ private final GenerationConfig configToBuild;
+
+ public Builder() {
+ configToBuild = new GenerationConfig();
+ }
+
+ public Builder withNPast(int n_past) {
+ configToBuild.n_past.set(n_past);
+ return this;
+ }
+
+ public Builder withNCtx(int n_ctx) {
+ configToBuild.n_ctx.set(n_ctx);
+ return this;
+ }
+
+ public Builder withNPredict(int n_predict) {
+ configToBuild.n_predict.set(n_predict);
+ return this;
+ }
+
+ public Builder withTopK(int top_k) {
+ configToBuild.top_k.set(top_k);
+ return this;
+ }
+
+ public Builder withTopP(float top_p) {
+ configToBuild.top_p.set(top_p);
+ return this;
+ }
+
+ public Builder withTemp(float temp) {
+ configToBuild.temp.set(temp);
+ return this;
+ }
+
+ public Builder withNBatch(int n_batch) {
+ configToBuild.n_batch.set(n_batch);
+ return this;
+ }
+
+ public Builder withRepeatPenalty(float repeat_penalty) {
+ configToBuild.repeat_penalty.set(repeat_penalty);
+ return this;
+ }
+
+ public Builder withRepeatLastN(int repeat_last_n) {
+ configToBuild.repeat_last_n.set(repeat_last_n);
+ return this;
+ }
+
+ public Builder withContextErase(float context_erase) {
+ configToBuild.context_erase.set(context_erase);
+ return this;
+ }
+
+ public GenerationConfig build() {
+ return configToBuild;
+ }
+ }
+ }
+
+ /**
+ * Shortcut for making GenerativeConfig builder.
+ */
+ public static GenerationConfig.Builder config(){
+ return new GenerationConfig.Builder();
+ }
+
+ /**
+ * This may be set before any Model instance classe are instantiated to
+ * set where the model may be found. This may be needed if setting
+ * library search path by standard means is not available.
+ */
+ public static String LIBRARY_SEARCH_PATH;
+
+
+ /**
+ * Generally for debugging purposes only. Will print
+ * the numerical tokens as they are generated instead of the string representations.
+ * Will also print out the processed input tokens as numbers to standard out.
+ */
+ public static boolean OUTPUT_DEBUG = false;
+
+ protected static LLModelLibrary library;
+
+ protected Pointer model;
+
+ protected String modelName;
+
+ public LLModel(Path modelPath) {
+
+ if(library==null) {
+
+ if (LIBRARY_SEARCH_PATH != null){
+ library = Util.loadSharedLibrary(LIBRARY_SEARCH_PATH);
+ library.llmodel_set_implementation_search_path(LIBRARY_SEARCH_PATH);
+ } else {
+ // Copy system libraries to Temp folder
+ Path tempLibraryDirectory = Util.copySharedLibraries();
+ library = Util.loadSharedLibrary(tempLibraryDirectory.toString());
+
+ library.llmodel_set_implementation_search_path(tempLibraryDirectory.toString() );
+ }
+
+ }
+
+ // modelType = type;
+ modelName = modelPath.getFileName().toString();
+ String modelPathAbs = modelPath.toAbsolutePath().toString();
+
+ LLModelLibrary.LLModelError error = new LLModelLibrary.LLModelError(jnr.ffi.Runtime.getSystemRuntime());
+
+ // Check if model file exists
+ if(!Files.exists(modelPath)){
+ throw new IllegalStateException("Model file does not exist: " + modelPathAbs);
+ }
+
+ // Create Model Struct. Will load dynamically the correct backend based on model type
+ model = library.llmodel_model_create2(modelPathAbs, "auto", error);
+
+ if(model == null) {
+ throw new IllegalStateException("Could not load gpt4all backend :" + error.message);
+ }
+ library.llmodel_loadModel(model, modelPathAbs);
+
+ if(!library.llmodel_isModelLoaded(model)){
+ throw new IllegalStateException("The model " + modelName + " could not be loaded");
+ }
+
+ }
+
+ public void setThreadCount(int nThreads) {
+ library.llmodel_setThreadCount(this.model, nThreads);
+ }
+
+ public int threadCount() {
+ return library.llmodel_threadCount(this.model);
+ }
+
+ /**
+ * Generate text after the prompt
+ *
+ * @param prompt The text prompt to complete
+ * @param generationConfig What generation settings to use while generating text
+ * @return String The complete generated text
+ */
+ public String generate(String prompt, GenerationConfig generationConfig) {
+ return generate(prompt, generationConfig, false);
+ }
+
+ /**
+ * Generate text after the prompt
+ *
+ * @param prompt The text prompt to complete
+ * @param generationConfig What generation settings to use while generating text
+ * @param streamToStdOut Should the generation be streamed to standard output. Useful for troubleshooting.
+ * @return String The complete generated text
+ */
+ public String generate(String prompt, GenerationConfig generationConfig, boolean streamToStdOut) {
+
+ ByteArrayOutputStream bufferingForStdOutStream = new ByteArrayOutputStream();
+ ByteArrayOutputStream bufferingForWholeGeneration = new ByteArrayOutputStream();
+
+ LLModelLibrary.ResponseCallback responseCallback = (int tokenID, Pointer response) -> {
+
+ if(LLModel.OUTPUT_DEBUG)
+ System.out.print("Response token " + tokenID + " " );
+
+ long len = 0;
+ byte nextByte;
+ do{
+ nextByte= response.getByte(len);
+ len++;
+ if(nextByte!=0) {
+ bufferingForWholeGeneration.write(nextByte);
+ if(streamToStdOut){
+ bufferingForStdOutStream.write(nextByte);
+ // Test if Buffer is UTF8 valid string.
+ byte[] currentBytes = bufferingForStdOutStream.toByteArray();
+ String validString = Util.getValidUtf8(currentBytes);
+ if(validString!=null){ // is valid string
+ System.out.print(validString);
+ // reset the buffer for next utf8 sequence to buffer
+ bufferingForStdOutStream.reset();
+ }
+ }
+ }
+ } while(nextByte != 0);
+
+ return true; // continue generating
+ };
+
+ library.llmodel_prompt(this.model,
+ prompt,
+ (int tokenID) -> {
+ if(LLModel.OUTPUT_DEBUG)
+ System.out.println("token " + tokenID);
+ return true; // continue processing
+ },
+ responseCallback,
+ (boolean isRecalculating) -> {
+ if(LLModel.OUTPUT_DEBUG)
+ System.out.println("recalculating");
+ return isRecalculating; // continue generating
+ },
+ generationConfig);
+
+ return bufferingForWholeGeneration.toString(StandardCharsets.UTF_8);
+ }
+
+
+
+ public static class ChatCompletionResponse {
+ public String model;
+ public Usage usage;
+ public List