DEV Community

Cover image for Java & GenAI : The Ultimate Developer's joy with Quarkus, Langchain4j and Ollama
Sebastien Blanc
Sebastien Blanc

Posted on

Java & GenAI : The Ultimate Developer's joy with Quarkus, Langchain4j and Ollama

This is the companion blog for this video
.

In this short post I will show you how to get started with Quarkus, Langchain4j and Ollama to set up your local dev environment to developer GenAI applications. Ready ? Let's go !

Installing Ollama

Ollama is one of those magic those that makes developers happy, basically you install it and then you can run LLMs locally, it has also a embedded web server so that you make requests to it, just like you would do with OpenAI.

Head over to Ollamaand follow the installation instructions.

Open a terminal and type :

ollama list

It will list you the LLMs that are installed locally on your computer. You can easily add a new LLM by doing for instance :

ollama pull mistral:latest

Check the model page to see what is available, it's that easy !

Creating the Quarkus project

Your Quarkus app will need for now only 2 extensions : quarkus-resteasy-reactive and quarkus-langchain4j-ollama , you can also use this link and click on the big blue button "Generate your application"

Once unzipped, open your project in your favorite IDE.

First steps with Langchain4j and Quarkus

We need to define in the properties which LLM we will be using, open your application.properties and add this line :

quarkus.langchain4j.ollama.chat-model.model-id=llama2:latest
Enter fullscreen mode Exit fullscreen mode

Now let's create our first AI Service, create a new interface called MyAIService.java :


package org.sebi;

import dev.langchain4j.service.UserMessage;
import io.quarki
verse.langchain4j.RegisterAiService;

@RegisterAiService
public interface MyAIService {
     public String chat(@UserMessage String message);
}

Enter fullscreen mode Exit fullscreen mode

The important thing to notice here is the @RegisterAiService annotation, this indicates to Quarkus that when calling the method chat it will be using the LLM that we defined in the properties.

Run your app : mvn quarkus:dev , this will start your application in dev mode.

Once it's running, press d in the terminal to open the devUI :

Image description

You should see the langchain4j widget containing a link called chat, you can click on it and it will bring you to an interface where can prompt your LLM !

At any point, you can change the model in the properties, you can also enable logging for the outgoing requests to see what is exactly sent to the LLM :

quarkus.langchain4j.ollama.chat-model.model-id=llama2:latest
quarkus.langchain4j.ollama.log-requests=true
Enter fullscreen mode Exit fullscreen mode

That's pretty cool, isn't it ?

Exposing our AI Service as a rest endpoint

The DevUI is useful but now we want to make it possible to access our LLM from a rest endpoint, we already have the resteasy-reactive extension and you should also have already a resource class called GreetingResource , change the content with :

package org.sebi;

import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/chat")
public class GreetingResource {

    @Inject
    MyAIService myAIService;

    @POST
    @Produces(MediaType.TEXT_PLAIN)
    @Consumes(MediaType.TEXT_PLAIN)
    public String hello(String body) {
        return myAIService.chat(body);
    }
}
Enter fullscreen mode Exit fullscreen mode

Here we simply inject our AI Service and call it in our POST method.

Try it out with this curl command :

curl -X POST  -H 'Content-Type: text/plain' -d 'Tell me a joke' http://localhost:8080/chat
Enter fullscreen mode Exit fullscreen mode

I got this one :

Sure, here's one:

Why don't scientists trust atoms?
Because they make up everything!
Enter fullscreen mode Exit fullscreen mode

Adding a system prompt

Let's fine tune our prompt by providing a system message. Usually you can use this to define how the chat bot must behave. Go to your AI Service and add this annotation :

     @SystemMessage("You are a funny bot and always reply with a poem!")   
     public String chat(@UserMessage String message);
Enter fullscreen mode Exit fullscreen mode

Try your curl command again and it should reply with a joke in the form of a poem !

You can change as you want the system prompt to experiment, change the model, all of this without having to stop&start your app because you are running in dev mode !

That all for today, next time we will explore how to add memory to our bot !

Top comments (1)

Collapse
 
selmantayyar profile image
Selman Tayyar

Thanks for the post. I am getting the error below on Windows. What could be going wrong?

`2024-03-08 12:20:23,461 INFO io.quarkus Installed features: [awt, cdi, langchain4j, langchain4j-ollama, poi, qute, rest-client-reactive, rest-client-reactive-jackson, resteasy-reactive, smallrye-context-propagation, vertx]
2024-03-08 12:20:44,197 INFO io.qua.lan.oll.OllamaRestApi$Ollam... Request:

  • method: POST
  • url: localhost:11434/api/generate
  • headers: [Accept: application/json], [Content-Type: application/json], [User-Agent: Resteasy Reactive Client], [content-length: 137]
  • body: {"model":"llama2:latest","prompt":"Tell me a joke","options":{"temperature":0.8,"num_predict":128,"top_p":0.9,"top_k":40},"stream":false} 2024-03-08 12:20:46,265 ERROR io.qua.lan.run.ais.AiServiceMethod... Execution of org.sebi.MyAIService#chat failed: org.jboss.resteasy.reactive.ClientWebApplicationException: Received: 'Internal Server Error, status code 500' when invoking: Rest Client method: 'io.quarkiverse.langchain4j.ollama.OllamaRestApi#generate' at org.jboss.resteasy.reactive.client.impl.RestClientRequestContext.unwrapException(RestClientRequestContext.java:194) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.handleException(AbstractResteasyReactiveContext.java:324) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:175) at org.jboss.resteasy.reactive.client.impl.RestClientRequestContext$1.lambda$execute$0(RestClientRequestContext.java:313) at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:276) at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:258) at io.vertx.core.impl.ContextInternal.lambda$runOnContext$0(ContextInternal.java:56) at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173) at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166) at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470) at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:566) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: jakarta.ws.rs.WebApplicationException: Internal Server Error, status code 500 at io.quarkus.rest.client.reactive.runtime.DefaultMicroprofileRestClientExceptionMapper.toThrowable(DefaultMicroprofileRestClientExceptionMapper.java:19) at io.quarkus.rest.client.reactive.runtime.MicroProfileRestClientResponseFilter.filter(MicroProfileRestClientResponseFilter.java:52) at org.jboss.resteasy.reactive.client.handlers.ClientResponseFilterRestHandler.handle(ClientResponseFilterRestHandler.java:21) at org.jboss.resteasy.reactive.client.handlers.ClientResponseFilterRestHandler.handle(ClientResponseFilterRestHandler.java:10) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.invokeHandler(AbstractResteasyReactiveContext.java:231) at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147) ... 12 more

2024-03-08 12:20:46,283 ERROR io.qua.ver.htt.run.QuarkusErrorHan... HTTP Request to /chat failed, error id: 8893484c-d8ac-463c-9e4f-c29831a5c82e-1: org.jboss.resteasy.reactive.ClientWebApplicationException: Received: 'Internal Server Error, status code 500' when invoking: Rest Client method: 'io.quarkiverse.langchain4j.ollama.OllamaRestApi#generate'
at org.jboss.resteasy.reactive.client.impl.RestClientRequestContext.unwrapException(RestClientRequestContext.java:194)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.handleException(AbstractResteasyReactiveContext.java:324)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:175)
at org.jboss.resteasy.reactive.client.impl.RestClientRequestContext$1.lambda$execute$0(RestClientRequestContext.java:313)
at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:276)
at io.vertx.core.impl.ContextInternal.dispatch(ContextInternal.java:258)
at io.vertx.core.impl.ContextInternal.lambda$runOnContext$0(ContextInternal.java:56)
at io.netty.util.concurrent.AbstractEventExecutor.runTask(AbstractEventExecutor.java:173)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:166)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:470)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:566)
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997)
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: jakarta.ws.rs.WebApplicationException: Internal Server Error, status code 500
at io.quarkus.rest.client.reactive.runtime.DefaultMicroprofileRestClientExceptionMapper.toThrowable(DefaultMicroprofileRestClientExceptionMapper.java:19)
at io.quarkus.rest.client.reactive.runtime.MicroProfileRestClientResponseFilter.filter(MicroProfileRestClientResponseFilter.java:52)
at org.jboss.resteasy.reactive.client.handlers.ClientResponseFilterRestHandler.handle(ClientResponseFilterRestHandler.java:21)
at org.jboss.resteasy.reactive.client.handlers.ClientResponseFilterRestHandler.handle(ClientResponseFilterRestHandler.java:10)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.invokeHandler(AbstractResteasyReactiveContext.java:231)
at org.jboss.resteasy.reactive.common.core.AbstractResteasyReactiveContext.run(AbstractResteasyReactiveContext.java:147)
... 12 more

`